Skip to content

Commit 8c38d91

Browse files
committed
PHP 8.3 | PSR12/ClassInstantiation: allow for readonly anonymous classes
The sniff is supposed to ignore anonymous class instantiations completely as otherwise it could create a conflict between this sniff and the PSR12 sniff checking anonymous class declarations. As things were, however, the sniff would add parentheses after the `new` keyword or after an attribute if an anonymous class was declared as `readonly`, as allowed since PHP 8.3. Fixed now. Includes minor simplification - the sniff would previously _jump over_ attributes attached to anonymous classes to get to the `class` keyword only to bow out later for anonymous classes anyway. Now, the sniff will bow out straight away when either an attribute, the `readonly` keyword or the `class` keyword for an anonymous class declaration is encountered. As `readonly` cannot be used as class name and attributes cannot be attached to (non-anonymous) class instantiations, this should maintain the previous behaviour, while also allowing for PHP 8.3 readonly anonymous classes and it should yield a very small performance benefit as well. Includes unit tests.
1 parent dc22022 commit 8c38d91

File tree

3 files changed

+14
-9
lines changed

3 files changed

+14
-9
lines changed

src/Standards/PSR12/Sniffs/Classes/ClassInstantiationSniff.php

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,14 @@ public function process(File $phpcsFile, $stackPtr)
6464
continue;
6565
}
6666

67-
// Skip over potential attributes for anonymous classes.
67+
// Bow out when this is an anonymous class.
68+
// Anonymous classes are the only situation which would allow for an attribute
69+
// or for the readonly keyword between "new" and the class "name".
6870
if ($tokens[$i]['code'] === T_ATTRIBUTE
69-
&& isset($tokens[$i]['attribute_closer']) === true
71+
|| $tokens[$i]['code'] === T_READONLY
72+
|| $tokens[$i]['code'] === T_ANON_CLASS
7073
) {
71-
$i = $tokens[$i]['attribute_closer'];
72-
continue;
74+
return;
7375
}
7476

7577
if ($tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET
@@ -87,11 +89,6 @@ public function process(File $phpcsFile, $stackPtr)
8789
return;
8890
}
8991

90-
if ($tokens[$classNameEnd]['code'] === T_ANON_CLASS) {
91-
// Ignore anon classes.
92-
return;
93-
}
94-
9592
if ($tokens[$classNameEnd]['code'] === T_OPEN_PARENTHESIS) {
9693
// Using parenthesis.
9794
return;

src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ $anonWithAttribute = new #[SomeAttribute('summary')] class {
4545

4646
$foo = new parent();
4747
$foo = new parent;
48+
49+
// PHP 8.3: safeguard that the sniff ignores anonymous classes, even when declared as readonly.
50+
$anon = new readonly class {};
51+
$anon = new #[MyAttribute] readonly class {};

src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc.fixed

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ $anonWithAttribute = new #[SomeAttribute('summary')] class {
4545

4646
$foo = new parent();
4747
$foo = new parent();
48+
49+
// PHP 8.3: safeguard that the sniff ignores anonymous classes, even when declared as readonly.
50+
$anon = new readonly class {};
51+
$anon = new #[MyAttribute] readonly class {};

0 commit comments

Comments
 (0)