Skip to content

Commit 11b9433

Browse files
authored
Add readonly constructor promotion support (#294)
* Add test for readonly constructor promotion * Add support for readonly * Adjust formatting
1 parent d8a00fb commit 11b9433

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

Tests/VariableAnalysisSniff/fixtures/ClassWithMembersFixture.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,15 @@ public static function createStatic($value) {
183183
}
184184

185185
}
186+
187+
class ClassWithReadonlyConstructorPromotion {
188+
public function __construct(
189+
private readonly string $message,
190+
private readonly $name,
191+
public readonly bool $key
192+
) {}
193+
194+
public function getMessage(): string {
195+
return $this->message;
196+
}
197+
}

VariableAnalysis/Lib/Helpers.php

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,24 +1445,38 @@ public static function isConstructorPromotion(File $phpcsFile, $stackPtr)
14451445

14461446
// If the previous token is a visibility keyword, this is constructor
14471447
// promotion. eg: `public $foobar`.
1448-
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), $functionIndex, true);
1449-
if (! is_int($prev)) {
1448+
$prevIndex = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), $functionIndex, true);
1449+
if (! is_int($prevIndex)) {
14501450
return false;
14511451
}
1452-
$prevToken = $tokens[$prev];
1452+
$prevToken = $tokens[$prevIndex];
14531453
if (in_array($prevToken['code'], Tokens::$scopeModifiers, true)) {
14541454
return true;
14551455
}
14561456

14571457
// If the previous token is not a visibility keyword, but the one before it
14581458
// is, the previous token was probably a typehint and this is constructor
14591459
// promotion. eg: `public boolean $foobar`.
1460-
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), $functionIndex, true);
1461-
if (! is_int($prev)) {
1460+
$prev2Index = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevIndex - 1), $functionIndex, true);
1461+
if (! is_int($prev2Index)) {
14621462
return false;
14631463
}
1464-
$prevToken = $tokens[$prev];
1465-
if (in_array($prevToken['code'], Tokens::$scopeModifiers, true)) {
1464+
$prev2Token = $tokens[$prev2Index];
1465+
if (in_array($prev2Token['code'], Tokens::$scopeModifiers, true)) {
1466+
return true;
1467+
}
1468+
1469+
// If the previous token is not a visibility keyword, but the one two
1470+
// before it is, and one of the tokens is `readonly`, the previous token
1471+
// was probably a typehint and this is constructor promotion. eg: `public
1472+
// readonly boolean $foobar`.
1473+
$prev3Index = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev2Index - 1), $functionIndex, true);
1474+
if (! is_int($prev3Index)) {
1475+
return false;
1476+
}
1477+
$prev3Token = $tokens[$prev3Index];
1478+
$wasPreviousReadonly = $prevToken['content'] === 'readonly' || $prev2Token['content'] === 'readonly';
1479+
if (in_array($prev3Token['code'], Tokens::$scopeModifiers, true) && $wasPreviousReadonly) {
14661480
return true;
14671481
}
14681482

0 commit comments

Comments
 (0)