Skip to content

Commit dc22022

Browse files
authored
Merge pull request #309 from PHPCSStandards/php-8.3/tokenizer-readonly-anonymous-classes
PHP 8.3 | Tokenizer/PHP: add support for readonly anonymous classes
2 parents 90ea70c + 9244858 commit dc22022

File tree

7 files changed

+69
-3
lines changed

7 files changed

+69
-3
lines changed

src/Tokenizers/PHP.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,23 @@ protected function tokenize($string)
621621
$preserveKeyword = true;
622622
}
623623

624+
// `new readonly class` should be preserved.
625+
if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW
626+
&& strtolower($token[1]) === 'readonly'
627+
) {
628+
for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
629+
if (is_array($tokens[$i]) === false
630+
|| isset(Util\Tokens::$emptyTokens[$tokens[$i][0]]) === false
631+
) {
632+
break;
633+
}
634+
}
635+
636+
if (is_array($tokens[$i]) === true && $tokens[$i][0] === T_CLASS) {
637+
$preserveKeyword = true;
638+
}
639+
}
640+
624641
// `new class extends` `new class implements` should be preserved
625642
if (($token[0] === T_EXTENDS || $token[0] === T_IMPLEMENTS)
626643
&& $finalTokens[$lastNotEmptyToken]['code'] === T_CLASS
@@ -1315,7 +1332,8 @@ protected function tokenize($string)
13151332

13161333
if ($tokenIsArray === true
13171334
&& strtolower($token[1]) === 'readonly'
1318-
&& isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === false
1335+
&& (isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === false
1336+
|| $finalTokens[$lastNotEmptyToken]['code'] === T_NEW)
13191337
) {
13201338
// Get the next non-whitespace token.
13211339
for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {

tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.inc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ $anonClass = new class {
55
function __construct() {}
66
};
77

8+
/* testReadonlyNoParentheses */
9+
$anonClass = new readonly class {
10+
function __construct() {}
11+
};
12+
813
/* testNoParenthesesAndEmptyTokens */
914
$anonClass = new class // phpcs:ignore Standard.Cat
1015
{
@@ -14,6 +19,11 @@ $anonClass = new class // phpcs:ignore Standard.Cat
1419
/* testWithParentheses */
1520
$anonClass = new class() {};
1621

22+
/* testReadonlyWithParentheses */
23+
$anonClass = new readonly class() {
24+
function __construct() {}
25+
};
26+
1727
/* testWithParenthesesAndEmptyTokens */
1828
$anonClass = new class /*comment */
1929
() {};

tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ public static function dataAnonClassNoParentheses()
7676
'plain' => [
7777
'testMarker' => '/* testNoParentheses */',
7878
],
79+
'readonly' => [
80+
'testMarker' => '/* testReadonlyNoParentheses */',
81+
],
7982
'declaration contains comments and extra whitespace' => [
8083
'testMarker' => '/* testNoParenthesesAndEmptyTokens */',
8184
],
@@ -139,6 +142,9 @@ public static function dataAnonClassWithParentheses()
139142
'plain' => [
140143
'testMarker' => '/* testWithParentheses */',
141144
],
145+
'readonly' => [
146+
'testMarker' => '/* testReadonlyWithParentheses */',
147+
],
142148
'declaration contains comments and extra whitespace' => [
143149
'testMarker' => '/* testWithParenthesesAndEmptyTokens */',
144150
],

tests/Core/Tokenizer/BackfillReadonlyTest.inc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,19 @@ class ReadonlyWithDisjunctiveNormalForm
138138
public function readonly (A&B $param): void {}
139139
}
140140

141+
/* testReadonlyAnonClassWithParens */
142+
$anon = new readonly class() {};
143+
144+
/* testReadonlyAnonClassWithoutParens */
145+
$anon = new Readonly class {};
146+
147+
/* testReadonlyAnonClassWithCommentsAndWhitespace */
148+
$anon = new
149+
// comment
150+
READONLY
151+
// phpcs:ignore Stnd.Cat.Sniff
152+
class {};
153+
141154
/* testParseErrorLiveCoding */
142155
// This must be the last test in the file.
143156
readonly

tests/Core/Tokenizer/BackfillReadonlyTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,17 @@ public static function dataReadonly()
151151
'property declaration, constructor property promotion, DNF type and reference' => [
152152
'testMarker' => '/* testReadonlyConstructorPropertyPromotionWithDNFAndReference */',
153153
],
154+
'anon class declaration, with parentheses' => [
155+
'testMarker' => '/* testReadonlyAnonClassWithParens */',
156+
],
157+
'anon class declaration, without parentheses' => [
158+
'testMarker' => '/* testReadonlyAnonClassWithoutParens */',
159+
'testContent' => 'Readonly',
160+
],
161+
'anon class declaration, with comments and whitespace' => [
162+
'testMarker' => '/* testReadonlyAnonClassWithCommentsAndWhitespace */',
163+
'testContent' => 'READONLY',
164+
],
154165
'live coding / parse error' => [
155166
'testMarker' => '/* testParseErrorLiveCoding */',
156167
],

tests/Core/Tokenizer/ContextSensitiveKeywordsTest.inc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,16 @@ namespace /* testNamespaceNameIsString1 */ my\ /* testNamespaceNameIsString2 */
9999
/* testVarIsKeyword */ var $var;
100100
/* testStaticIsKeyword */ static $static;
101101

102-
/* testReadonlyIsKeyword */ readonly $readonly;
102+
/* testReadonlyIsKeywordForProperty */ readonly $readonly;
103103

104104
/* testFinalIsKeyword */ final /* testFunctionIsKeyword */ function someFunction(
105105
/* testCallableIsKeyword */
106106
callable $callable,
107107
) {
108+
$anon = new /* testReadonlyIsKeywordForAnonClass */ readonly class() {
109+
public function foo() {}
110+
};
111+
108112
/* testReturnIsKeyword */
109113
return $this;
110114
}

tests/Core/Tokenizer/ContextSensitiveKeywordsTest.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public static function dataKeywords()
232232
'expectedTokenType' => 'T_STATIC',
233233
],
234234
'readonly: property declaration' => [
235-
'testMarker' => '/* testReadonlyIsKeyword */',
235+
'testMarker' => '/* testReadonlyIsKeywordForProperty */',
236236
'expectedTokenType' => 'T_READONLY',
237237
],
238238
'final: function declaration' => [
@@ -247,6 +247,10 @@ public static function dataKeywords()
247247
'testMarker' => '/* testCallableIsKeyword */',
248248
'expectedTokenType' => 'T_CALLABLE',
249249
],
250+
'readonly: anon class declaration' => [
251+
'testMarker' => '/* testReadonlyIsKeywordForAnonClass */',
252+
'expectedTokenType' => 'T_READONLY',
253+
],
250254
'return: statement' => [
251255
'testMarker' => '/* testReturnIsKeyword */',
252256
'expectedTokenType' => 'T_RETURN',

0 commit comments

Comments
 (0)