From 9316af0f331782047026b63d03238c2afa42f40c Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Nov 2025 01:43:05 +0100 Subject: [PATCH 1/3] Add `TwigComponent\ForbiddenAttributesPropertyRule` --- README.md | 70 +++++++++++++++- src/NodeAnalyzer/AttributeFinder.php | 49 +++++++++++ .../ForbiddenAttributesPropertyRule.php | 81 +++++++++++++++++++ .../ComponentWithAttributesProperty.php | 13 +++ .../ComponentWithCustomAttributesProperty.php | 13 +++ .../ComponentWithNoAttributesProperty.php | 12 +++ .../Fixture/NotAComponent.php | 10 +++ .../ForbiddenAttributesPropertyRuleTest.php | 54 +++++++++++++ 8 files changed, 300 insertions(+), 2 deletions(-) create mode 100644 src/NodeAnalyzer/AttributeFinder.php create mode 100644 src/Rules/TwigComponent/ForbiddenAttributesPropertyRule.php create mode 100644 tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithAttributesProperty.php create mode 100644 tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithCustomAttributesProperty.php create mode 100644 tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithNoAttributesProperty.php create mode 100644 tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/NotAComponent.php create mode 100644 tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/ForbiddenAttributesPropertyRuleTest.php diff --git a/README.md b/README.md index 1ddb5d0..0dff976 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,72 @@ To install the PHPStan rules for Symfony UX, you can use Composer: composer require --dev kocal/phpstan-symfony-ux ``` -## Configuration +## TwigComponent Rules -TODO \ No newline at end of file +### ForbiddenAttributesPropertyRule + +Forbid the use of the `$attributes` property in Twig Components, which can lead to confusion when using `{{ attributes }}` (an instance of `ComponentAttributes` that is automatically injected) in Twig templates. + +```yaml +rules: + - Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ForbiddenAttributesPropertyRule +``` + +```php +// src/Twig/Components/Alert.php +namespace App\Twig\Components; + +use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; + +#[AsTwigComponent] +final class Alert +{ + public $attributes; +} +``` + +```php +// src/Twig/Components/Alert.php +namespace App\Twig\Components; + +use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; + +#[AsTwigComponent(attributesVar: 'customAttributes')] +final class Alert +{ + public $customAttributes; +} +``` + +:x: + +
+ +```php +// src/Twig/Components/Alert.php +namespace App\Twig\Components; + +use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; + +#[AsTwigComponent] +final class Alert +{ +} +``` + +```php +// src/Twig/Components/Alert.php +namespace App\Twig\Components; + +use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; + +#[AsTwigComponent] +final class Alert +{ + public $customAttributes; +} +``` + +:+1: + +
diff --git a/src/NodeAnalyzer/AttributeFinder.php b/src/NodeAnalyzer/AttributeFinder.php new file mode 100644 index 0000000..ddf7f0e --- /dev/null +++ b/src/NodeAnalyzer/AttributeFinder.php @@ -0,0 +1,49 @@ +attrGroups as $attrGroup) { + $attributes = array_merge($attributes, $attrGroup->attrs); + } + + return $attributes; + } + + public static function findAttribute(ClassMethod | Property | ClassLike | Param $node, string $desiredAttributeClass): ?Attribute + { + $attributes = self::findAttributes($node); + + foreach ($attributes as $attribute) { + if (! $attribute->name instanceof FullyQualified) { + continue; + } + + if ($attribute->name->toString() === $desiredAttributeClass) { + return $attribute; + } + } + + return null; + } +} diff --git a/src/Rules/TwigComponent/ForbiddenAttributesPropertyRule.php b/src/Rules/TwigComponent/ForbiddenAttributesPropertyRule.php new file mode 100644 index 0000000..b48c6ac --- /dev/null +++ b/src/Rules/TwigComponent/ForbiddenAttributesPropertyRule.php @@ -0,0 +1,81 @@ + + */ +final class ForbiddenAttributesPropertyRule implements Rule +{ + public function getNodeType(): string + { + return Class_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (! $asTwigComponent = AttributeFinder::findAttribute($node, AsTwigComponent::class)) { + return []; + } + + if (! $attributesVarName = $this->getAttributesVarName($asTwigComponent)) { + return []; + } + + if ($propertyAttributes = $node->getProperty($attributesVarName['name'])) { + return [ + RuleErrorBuilder::message( + $attributesVarName['custom'] + ? sprintf('Using property "%s" in a Twig component is forbidden, it may lead to confusion with the "%s" attribute defined in #[AsTwigComponent].', $attributesVarName['name'], $attributesVarName['name']) + : sprintf('Using property "%s" in a Twig component is forbidden, it may lead to confusion with the default "attributes" Twig variable.', $attributesVarName['name']) + ) + ->identifier('SymfonyUX.TwigComponent.forbiddenAttributesProperty') + ->line($propertyAttributes->getLine()) + ->tip('Consider renaming or removing this property to avoid conflicts with the Twig component attributes.') + ->build(), + + ]; + } + + return []; + } + + /** + * @return {name: string, custom: false}|null + */ + private function getAttributesVarName(Node\Attribute $attribute): ?array + { + foreach ($attribute->args as $arg) { + if ($arg->name && $arg->name->toString() === 'attributesVar') { + if ($arg->value instanceof Node\Scalar\String_) { + return [ + 'name' => $arg->value->value, + 'custom' => true, + ]; + } + } + } + + $reflAttribute = new \ReflectionClass(AsTwigComponent::class); + foreach ($reflAttribute->getConstructor()->getParameters() as $reflParameter) { + if ($reflParameter->getName() === 'attributesVar' && $reflParameter->isDefaultValueAvailable()) { + return [ + 'name' => $reflParameter->getDefaultValue(), + 'custom' => false, + ]; + } + } + + return null; + } +} diff --git a/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithAttributesProperty.php b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithAttributesProperty.php new file mode 100644 index 0000000..efe3665 --- /dev/null +++ b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithAttributesProperty.php @@ -0,0 +1,13 @@ +analyse( + [__DIR__ . '/Fixture/ComponentWithAttributesProperty.php'], + [ + [ + 'Using property "attributes" in a Twig component is forbidden, it may lead to confusion with the default "attributes" Twig variable.', + 12, + 'Consider renaming or removing this property to avoid conflicts with the Twig component attributes.', + ], + ] + ); + + $this->analyse( + [__DIR__ . '/Fixture/ComponentWithCustomAttributesProperty.php'], + [ + [ + 'Using property "customAttributes" in a Twig component is forbidden, it may lead to confusion with the "customAttributes" attribute defined in #[AsTwigComponent].', + 12, + 'Consider renaming or removing this property to avoid conflicts with the Twig component attributes.', + ], + ] + ); + } + + public function testNoViolations(): void + { + $this->analyse( + [__DIR__ . '/Fixture/NotAComponent.php'], + [] + ); + $this->analyse( + [__DIR__ . '/Fixture/ComponentWithNoAttributesProperty.php'], + [] + ); + } + + protected function getRule(): Rule + { + return new ForbiddenAttributesPropertyRule(); + } +} From 09dcbc76be7e9bd8275198060b44efbcd6081e1c Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Nov 2025 01:43:11 +0100 Subject: [PATCH 2/3] Add `TwigComponent\ForbiddenClassPropertyRule` --- README.md | 43 ++++++++++++++++++ .../ForbiddenClassPropertyRule.php | 44 +++++++++++++++++++ .../Fixture/ComponentWithClassProperty.php | 13 ++++++ .../Fixture/ComponentWithNoClassProperty.php | 12 +++++ .../Fixture/NotAComponent.php | 10 +++++ .../ForbiddenClassPropertyRuleTest.php | 43 ++++++++++++++++++ 6 files changed, 165 insertions(+) create mode 100644 src/Rules/TwigComponent/ForbiddenClassPropertyRule.php create mode 100644 tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/ComponentWithClassProperty.php create mode 100644 tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/ComponentWithNoClassProperty.php create mode 100644 tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/NotAComponent.php create mode 100644 tests/Rules/TwigComponent/ForbiddenClassPropertyRule/ForbiddenClassPropertyRuleTest.php diff --git a/README.md b/README.md index 0dff976..0221114 100644 --- a/README.md +++ b/README.md @@ -79,3 +79,46 @@ final class Alert :+1:
+ +### ForbiddenClassPropertyRule + +Forbid the use of the `$class` property in Twig Components, as it is considered a bad practice to manipulate CSS classes directly in components. +Use `{{ attributes }}` or `{{ attributes.defaults({ class: '...' }) }}` in your Twig templates instead. + +```yaml +rules: + - Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ForbiddenClassPropertyRule +``` + +```php +// src/Twig/Components/Alert.php +namespace App\Twig\Components; + +use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; + +#[AsTwigComponent] +final class Alert +{ + public $class; +} +``` + +:x: + +
+ +```php +// src/Twig/Components/Alert.php +namespace App\Twig\Components; + +use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; + +#[AsTwigComponent] +final class Alert +{ +} +``` + +:+1: + +
diff --git a/src/Rules/TwigComponent/ForbiddenClassPropertyRule.php b/src/Rules/TwigComponent/ForbiddenClassPropertyRule.php new file mode 100644 index 0000000..810e5ec --- /dev/null +++ b/src/Rules/TwigComponent/ForbiddenClassPropertyRule.php @@ -0,0 +1,44 @@ + + */ +final class ForbiddenClassPropertyRule implements Rule +{ + public function getNodeType(): string + { + return Class_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (! AttributeFinder::findAttribute($node, AsTwigComponent::class)) { + return []; + } + + if ($propertyClass = $node->getProperty('class')) { + return [ + RuleErrorBuilder::message('Using a "class" property in a Twig component is forbidden, it is considered as an anti-pattern.') + ->identifier('symfonyUX.twigComponent.forbiddenClassProperty') + ->line($propertyClass->getLine()) + ->tip('Consider using {{ attributes }} to automatically render unknown properties as HTML attributes, such as "class". Learn more at https://symfony.com/bundles/ux-twig-component/current/index.html#component-attributes.') + ->build(), + + ]; + } + + return []; + } +} diff --git a/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/ComponentWithClassProperty.php b/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/ComponentWithClassProperty.php new file mode 100644 index 0000000..4069d8b --- /dev/null +++ b/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/ComponentWithClassProperty.php @@ -0,0 +1,13 @@ +analyse( + [__DIR__ . '/Fixture/ComponentWithClassProperty.php'], + [ + [ + 'Using a "class" property in a Twig component is forbidden, it is considered as an anti-pattern.', + 12, + 'Consider using {{ attributes }} to automatically render unknown properties as HTML attributes, such as "class". Learn more at https://symfony.com/bundles/ux-twig-component/current/index.html#component-attributes.', + ], + ] + ); + } + + public function testNoViolations(): void + { + $this->analyse( + [__DIR__ . '/Fixture/NotAComponent.php'], + [] + ); + $this->analyse( + [__DIR__ . '/Fixture/ComponentWithNoClassProperty.php'], + [] + ); + } + + protected function getRule(): Rule + { + return new ForbiddenClassPropertyRule(); + } +} From 4f5e97fbaf363e21386ecae3e45dad238171c4dc Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Nov 2025 10:41:15 +0100 Subject: [PATCH 3/3] Minor improvements --- .github/workflows/ci.yaml | 7 ++----- composer.json | 7 ++++++- src/NodeAnalyzer/AttributeFinder.php | 2 +- .../ForbiddenAttributesPropertyRule.php | 16 +++++++++++----- .../Fixture/ComponentWithAttributesProperty.php | 2 +- .../ComponentWithCustomAttributesProperty.php | 2 +- .../Fixture/NotAComponent.php | 5 ++++- .../ForbiddenAttributesPropertyRuleTest.php | 11 ++++++++++- .../config/configured_rule.neon | 2 ++ .../Fixture/ComponentWithClassProperty.php | 2 +- .../Fixture/NotAComponent.php | 2 +- .../ForbiddenClassPropertyRuleTest.php | 11 ++++++++++- .../config/configured_rule.neon | 2 ++ 13 files changed, 52 insertions(+), 19 deletions(-) create mode 100644 tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/config/configured_rule.neon create mode 100644 tests/Rules/TwigComponent/ForbiddenClassPropertyRule/config/configured_rule.neon diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bea5c27..86bfa10 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -56,15 +56,12 @@ jobs: name: Tests runs-on: ubuntu-latest strategy: + fail-fast: false matrix: php: [ '8.2', '8.3', '8.4', '8.5' ] composer-dependency-version: [''] composer-minimum-stability: ['stable'] include: - # Lowest dependencies on minimum supported PHP version - - php: '8.2' - composer-dependency-version: 'lowest' - # Highest dev dependencies - php: '8.5' composer-minimum-stability: 'dev' @@ -83,7 +80,7 @@ jobs: run: symfony composer config minimum-stability ${{ matrix.composer-minimum-stability }} - name: Install Composer dependencies - run: symfony composer update --prefer-dist --no-interaction --no-progress ${{ matrix.composer-dependency-version == 'lowest' && '--prefer-lowest' || '' }} + run: symfony composer update --prefer-dist --no-interaction --no-progress - name: Run PHPUnit tests run: symfony composer run test diff --git a/composer.json b/composer.json index 73c5a38..dca6916 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,11 @@ } ], "scripts": { + "qa-fix": [ + "@cs-fix", + "@phpstan", + "@test" + ], "phpstan": "vendor/bin/phpstan analyze", "test": "vendor/bin/phpunit", "cs": "vendor/bin/ecs check", @@ -30,7 +35,7 @@ "phpstan/phpstan": "^2.1.13" }, "require-dev": { - "phpunit/phpunit": "^11.0", + "phpunit/phpunit": "^11.1", "symfony/ux-twig-component": "^2.0", "symplify/easy-coding-standard": "^13.0" }, diff --git a/src/NodeAnalyzer/AttributeFinder.php b/src/NodeAnalyzer/AttributeFinder.php index ddf7f0e..3329697 100644 --- a/src/NodeAnalyzer/AttributeFinder.php +++ b/src/NodeAnalyzer/AttributeFinder.php @@ -8,8 +8,8 @@ use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassLike; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; -use PHPStan\Node\ClassMethod; /** * Heavily inspired by https://github.com/symplify/phpstan-rules/blob/main/src/NodeAnalyzer/AttributeFinder.php <3 diff --git a/src/Rules/TwigComponent/ForbiddenAttributesPropertyRule.php b/src/Rules/TwigComponent/ForbiddenAttributesPropertyRule.php index b48c6ac..25f9a44 100644 --- a/src/Rules/TwigComponent/ForbiddenAttributesPropertyRule.php +++ b/src/Rules/TwigComponent/ForbiddenAttributesPropertyRule.php @@ -8,6 +8,7 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; @@ -17,6 +18,11 @@ */ final class ForbiddenAttributesPropertyRule implements Rule { + public function __construct( + private ReflectionProvider $reflectionProvider, + ) { + } + public function getNodeType(): string { return Class_::class; @@ -51,7 +57,7 @@ public function processNode(Node $node, Scope $scope): array } /** - * @return {name: string, custom: false}|null + * @return array{name: string, custom: bool}|null */ private function getAttributesVarName(Node\Attribute $attribute): ?array { @@ -66,11 +72,11 @@ private function getAttributesVarName(Node\Attribute $attribute): ?array } } - $reflAttribute = new \ReflectionClass(AsTwigComponent::class); - foreach ($reflAttribute->getConstructor()->getParameters() as $reflParameter) { - if ($reflParameter->getName() === 'attributesVar' && $reflParameter->isDefaultValueAvailable()) { + $reflAttribute = $this->reflectionProvider->getClass(AsTwigComponent::class); + foreach ($reflAttribute->getConstructor()->getOnlyVariant()->getParameters() as $reflParameter) { + if ($reflParameter->getName() === 'attributesVar' && $reflParameter->getDefaultValue()?->getConstantStrings()) { return [ - 'name' => $reflParameter->getDefaultValue(), + 'name' => $reflParameter->getDefaultValue()->getConstantStrings()[0]->getValue(), 'custom' => false, ]; } diff --git a/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithAttributesProperty.php b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithAttributesProperty.php index efe3665..3fe535f 100644 --- a/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithAttributesProperty.php +++ b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithAttributesProperty.php @@ -9,5 +9,5 @@ #[AsTwigComponent] final class ComponentWithAttributesProperty { - public $attributes; + public string $attributes; } diff --git a/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithCustomAttributesProperty.php b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithCustomAttributesProperty.php index db24cc7..8fc7fea 100644 --- a/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithCustomAttributesProperty.php +++ b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/ComponentWithCustomAttributesProperty.php @@ -9,5 +9,5 @@ #[AsTwigComponent(attributesVar: 'customAttributes')] final class ComponentWithAttributesProperty { - public $customAttributes; + public string $customAttributes; } diff --git a/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/NotAComponent.php b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/NotAComponent.php index aaa0bcf..42bcf49 100644 --- a/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/NotAComponent.php +++ b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/Fixture/NotAComponent.php @@ -6,5 +6,8 @@ final class NotAComponent { - public $attributes; + /** + * @var array + */ + public array $attributes; } diff --git a/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/ForbiddenAttributesPropertyRuleTest.php b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/ForbiddenAttributesPropertyRuleTest.php index e0cd14e..ded3931 100644 --- a/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/ForbiddenAttributesPropertyRuleTest.php +++ b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/ForbiddenAttributesPropertyRuleTest.php @@ -8,6 +8,9 @@ use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; +/** + * @extends RuleTestCase + */ final class ForbiddenAttributesPropertyRuleTest extends RuleTestCase { public function testViolations(): void @@ -41,14 +44,20 @@ public function testNoViolations(): void [__DIR__ . '/Fixture/NotAComponent.php'], [] ); + $this->analyse( [__DIR__ . '/Fixture/ComponentWithNoAttributesProperty.php'], [] ); } + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/configured_rule.neon']; + } + protected function getRule(): Rule { - return new ForbiddenAttributesPropertyRule(); + return self::getContainer()->getByType(ForbiddenAttributesPropertyRule::class); } } diff --git a/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/config/configured_rule.neon b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/config/configured_rule.neon new file mode 100644 index 0000000..be4a372 --- /dev/null +++ b/tests/Rules/TwigComponent/ForbiddenAttributesPropertyRule/config/configured_rule.neon @@ -0,0 +1,2 @@ +rules: + - Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ForbiddenAttributesPropertyRule diff --git a/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/ComponentWithClassProperty.php b/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/ComponentWithClassProperty.php index 4069d8b..59ba5da 100644 --- a/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/ComponentWithClassProperty.php +++ b/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/ComponentWithClassProperty.php @@ -9,5 +9,5 @@ #[AsTwigComponent] final class ComponentWithClassProperty { - public $class; + public string $class; } diff --git a/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/NotAComponent.php b/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/NotAComponent.php index 4dcb913..e3d02f7 100644 --- a/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/NotAComponent.php +++ b/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/Fixture/NotAComponent.php @@ -6,5 +6,5 @@ final class NotAComponent { - public $attributes; + public string $class; } diff --git a/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/ForbiddenClassPropertyRuleTest.php b/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/ForbiddenClassPropertyRuleTest.php index abeb195..583573a 100644 --- a/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/ForbiddenClassPropertyRuleTest.php +++ b/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/ForbiddenClassPropertyRuleTest.php @@ -8,6 +8,9 @@ use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; +/** + * @extends RuleTestCase + */ final class ForbiddenClassPropertyRuleTest extends RuleTestCase { public function testViolations(): void @@ -30,14 +33,20 @@ public function testNoViolations(): void [__DIR__ . '/Fixture/NotAComponent.php'], [] ); + $this->analyse( [__DIR__ . '/Fixture/ComponentWithNoClassProperty.php'], [] ); } + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/configured_rule.neon']; + } + protected function getRule(): Rule { - return new ForbiddenClassPropertyRule(); + return self::getContainer()->getByType(ForbiddenClassPropertyRule::class); } } diff --git a/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/config/configured_rule.neon b/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/config/configured_rule.neon new file mode 100644 index 0000000..b719d97 --- /dev/null +++ b/tests/Rules/TwigComponent/ForbiddenClassPropertyRule/config/configured_rule.neon @@ -0,0 +1,2 @@ +rules: + - Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ForbiddenClassPropertyRule