File tree Expand file tree Collapse file tree 5 files changed +131
-0
lines changed
tests/PHPStan/Rules/Classes Expand file tree Collapse file tree 5 files changed +131
-0
lines changed Original file line number Diff line number Diff line change 5656 - PHPStan\Rules\Classes\LocalTypeTraitAliasesRule
5757 - PHPStan\Rules\Classes\NewStaticRule
5858 - PHPStan\Rules\Classes\NonClassAttributeClassRule
59+ - PHPStan\Rules\Classes\ReadOnlyClassRule
5960 - PHPStan\Rules\Classes\TraitAttributeClassRule
6061 - PHPStan\Rules\Constants\DynamicClassConstantFetchRule
6162 - PHPStan\Rules\Constants\FinalConstantRule
Original file line number Diff line number Diff line change @@ -262,4 +262,14 @@ public function supportsDynamicClassConstantFetch(): bool
262262 return $ this ->versionId >= 80300 ;
263263 }
264264
265+ public function supportsReadOnlyClasses (): bool
266+ {
267+ return $ this ->versionId >= 80200 ;
268+ }
269+
270+ public function supportsReadOnlyAnonymousClasses (): bool
271+ {
272+ return $ this ->versionId >= 80300 ;
273+ }
274+
265275}
Original file line number Diff line number Diff line change 1+ <?php declare (strict_types = 1 );
2+
3+ namespace PHPStan \Rules \Classes ;
4+
5+ use PhpParser \Node ;
6+ use PHPStan \Analyser \Scope ;
7+ use PHPStan \Node \InClassNode ;
8+ use PHPStan \Php \PhpVersion ;
9+ use PHPStan \Rules \Rule ;
10+ use PHPStan \Rules \RuleErrorBuilder ;
11+
12+ /**
13+ * @implements Rule<InClassNode>
14+ */
15+ class ReadOnlyClassRule implements Rule
16+ {
17+
18+ public function __construct (private PhpVersion $ phpVersion )
19+ {
20+ }
21+
22+ public function getNodeType (): string
23+ {
24+ return InClassNode::class;
25+ }
26+
27+ public function processNode (Node $ node , Scope $ scope ): array
28+ {
29+ $ classReflection = $ node ->getClassReflection ();
30+ if (!$ classReflection ->isReadOnly ()) {
31+ return [];
32+ }
33+ if ($ classReflection ->isAnonymous ()) {
34+ if ($ this ->phpVersion ->supportsReadOnlyAnonymousClasses ()) {
35+ return [];
36+ }
37+
38+ return [
39+ RuleErrorBuilder::message ('Anonymous readonly classes are supported only on PHP 8.3 and later. ' )
40+ ->identifier ('classConstant.nativeTypeNotSupported ' )
41+ ->nonIgnorable ()
42+ ->build (),
43+ ];
44+ }
45+
46+ if ($ this ->phpVersion ->supportsReadOnlyClasses ()) {
47+ return [];
48+ }
49+
50+ return [
51+ RuleErrorBuilder::message ('Readonly classes are supported only on PHP 8.2 and later. ' )
52+ ->identifier ('classConstant.nativeTypeNotSupported ' )
53+ ->nonIgnorable ()
54+ ->build (),
55+ ];
56+ }
57+
58+ }
Original file line number Diff line number Diff line change 1+ <?php declare (strict_types = 1 );
2+
3+ namespace PHPStan \Rules \Classes ;
4+
5+ use PHPStan \Php \PhpVersion ;
6+ use PHPStan \Rules \Rule as TRule ;
7+ use PHPStan \Testing \RuleTestCase ;
8+ use const PHP_VERSION_ID ;
9+
10+ /**
11+ * @extends RuleTestCase<ReadOnlyClassRule>
12+ */
13+ class ReadOnlyClassRuleTest extends RuleTestCase
14+ {
15+
16+ protected function getRule (): TRule
17+ {
18+ return new ReadOnlyClassRule (self ::getContainer ()->getByType (PhpVersion::class));
19+ }
20+
21+ public function testRule (): void
22+ {
23+ $ errors = [];
24+ if (PHP_VERSION_ID < 80200 ) {
25+ $ errors = [
26+ [
27+ 'Readonly classes are supported only on PHP 8.2 and later. ' ,
28+ 5 ,
29+ ],
30+ ];
31+ } elseif (PHP_VERSION_ID < 80300 ) {
32+ $ errors = [
33+ [
34+ 'Anonymous readonly classes are supported only on PHP 8.3 and later. ' ,
35+ 15 ,
36+ ],
37+ ];
38+ }
39+ $ this ->analyse ([__DIR__ . '/data/readonly-class.php ' ], $ errors );
40+ }
41+
42+ }
Original file line number Diff line number Diff line change 1+ <?php // lint >= 8.3
2+
3+ namespace ReadonlyClass ;
4+
5+ readonly class Foo
6+ {
7+
8+ }
9+
10+ class Bar
11+ {
12+
13+ public function doFoo (): void
14+ {
15+ $ c = new readonly class () {
16+
17+ };
18+ }
19+
20+ }
You can’t perform that action at this time.
0 commit comments