Skip to content

Commit 63f69df

Browse files
Handling final for abstract classes (#11)
1 parent 7c4ab4a commit 63f69df

File tree

5 files changed

+57
-1
lines changed

5 files changed

+57
-1
lines changed

data/Service/AbstractServiceClass.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace App\Service;
4+
5+
abstract class AbstractServiceClass
6+
{
7+
abstract public function doSomething(): void;
8+
}

docs/Rules.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,14 @@ Ensures that classes matching specified patterns are declared as `final`.
7979
class: Phauthentic\PHPStanRules\Architecture\ClassMustBeFinalRule
8080
arguments:
8181
patterns: ['/^App\\Service\\/']
82+
ignoreAbstractClasses: true
8283
tags:
8384
- phpstan.rules.rule
8485
```
8586

87+
- `patterns`: Array of regex patterns to match against class names.
88+
- `ignoreAbstractClasses`: Whether to ignore abstract classes (default: `true`).
89+
8690
## Namespace Class Pattern Rule {#namespace-class-pattern-rule}
8791

8892
Ensures that classes inside namespaces matching a given regex must have names matching at least one of the provided patterns.

src/Architecture/ClassMustBeFinalRule.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class ClassMustBeFinalRule implements Rule
2929
* Each pattern should be a valid PCRE regex.
3030
*/
3131
public function __construct(
32-
protected array $patterns
32+
protected array $patterns,
33+
protected bool $ignoreAbstractClasses = true
3334
) {
3435
}
3536

@@ -47,6 +48,11 @@ public function processNode(Node $node, Scope $scope): array
4748
return [];
4849
}
4950

51+
// Skip abstract classes if configured to ignore them
52+
if ($this->ignoreAbstractClasses && $node->isAbstract()) {
53+
return [];
54+
}
55+
5056
$className = $node->name->toString();
5157
$namespaceName = $scope->getNamespace() ?? '';
5258
$fullClassName = $namespaceName . '\\' . $className;

tests/TestCases/Architecture/ClassMustBeFinalRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,9 @@ public function testRule(): void
2828
],
2929
]);
3030
}
31+
32+
public function testRuleIgnoresAbstractClassesByDefault(): void
33+
{
34+
$this->analyse([__DIR__ . '/../../../data/Service/AbstractServiceClass.php'], []);
35+
}
3136
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Phauthentic\PHPStanRules\Tests\TestCases\Architecture;
6+
7+
use Phauthentic\PHPStanRules\Architecture\ClassMustBeFinalRule;
8+
use PHPStan\Testing\RuleTestCase;
9+
10+
/**
11+
* @extends RuleTestCase<ClassMustBeFinalRule>
12+
*/
13+
class ClassMustBeFinalRuleWithAbstractClassesTest extends RuleTestCase
14+
{
15+
protected function getRule(): \PHPStan\Rules\Rule
16+
{
17+
return new ClassMustBeFinalRule([
18+
'/ServiceClass$/', // all classes that end with "ServiceClass"
19+
], false); // Don't ignore abstract classes
20+
}
21+
22+
public function testRuleWithAbstractClassesNotIgnored(): void
23+
{
24+
$this->analyse([__DIR__ . '/../../../data/Service/AbstractServiceClass.php'], [
25+
[
26+
'Class App\Service\AbstractServiceClass must be final.',
27+
5,
28+
],
29+
]);
30+
}
31+
32+
33+
}

0 commit comments

Comments
 (0)