Skip to content

Commit 0774fab

Browse files
authored
PromotedConstructorPropertyFixer - handle property hooks (#1052)
1 parent 2d9af89 commit 0774fab

File tree

3 files changed

+79
-19
lines changed

3 files changed

+79
-19
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![Latest stable version](https://img.shields.io/packagist/v/kubawerlos/php-cs-fixer-custom-fixers.svg?label=current%20version)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers)
66
[![PHP version](https://img.shields.io/packagist/php-v/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://php.net)
77
[![License](https://img.shields.io/github/license/kubawerlos/php-cs-fixer-custom-fixers.svg)](LICENSE)
8-
![Tests](https://img.shields.io/badge/tests-3799-brightgreen.svg)
8+
![Tests](https://img.shields.io/badge/tests-3800-brightgreen.svg)
99
[![Downloads](https://img.shields.io/packagist/dt/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers)
1010

1111
[![CI status](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/actions/workflows/ci.yaml/badge.svg)](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/actions/workflows/ci.yaml)

src/Fixer/PromotedConstructorPropertyFixer.php

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,16 @@ private function promoteProperties(Tokens $tokens, int $classIndex, ConstructorA
167167
continue;
168168
}
169169

170-
$tokensToInsert = self::removePropertyAndReturnTokensToInsert($tokens, $propertyIndex);
170+
[$tokensToInsertBefore, $tokensToInsertAfter] = self::removePropertyAndReturnTokensToInsert($tokens, $propertyIndex);
171171

172172
self::renameVariable($tokens, $constructorAnalysis->getConstructorIndex(), $oldParameterName, $newParameterName);
173173

174174
self::removeAssignment($tokens, $constructorPromotableAssignments[$constructorParameterName]);
175175
$this->updateParameterSignature(
176176
$tokens,
177177
$constructorParameterIndex,
178-
$tokensToInsert,
178+
$tokensToInsertBefore,
179+
$tokensToInsertAfter,
179180
\substr($propertyType, 0, 1) === '?',
180181
);
181182
}
@@ -316,12 +317,12 @@ private static function getClassProperties(Tokens $tokens, int $classIndex): arr
316317
}
317318

318319
/**
319-
* @return list<Token>
320+
* @return array{list<Token>, list<Token>}
320321
*/
321322
private static function removePropertyAndReturnTokensToInsert(Tokens $tokens, ?int $propertyIndex): array
322323
{
323324
if ($propertyIndex === null) {
324-
return [new Token([\T_PUBLIC, 'public'])];
325+
return [[new Token([\T_PUBLIC, 'public'])], []];
325326
}
326327

327328
$visibilityIndex = $tokens->getPrevTokenOfKind($propertyIndex, [[\T_PRIVATE], [\T_PROTECTED], [\T_PUBLIC], [\T_VAR]]);
@@ -342,21 +343,28 @@ private static function removePropertyAndReturnTokensToInsert(Tokens $tokens, ?i
342343
$removeFrom++;
343344
}
344345

345-
$tokensToInsert = [];
346+
$tokensToInsertBefore = [];
346347
for ($index = $removeFrom; $index <= $visibilityIndex - 1; $index++) {
347-
$tokensToInsert[] = $tokens[$index];
348+
$tokensToInsertBefore[] = $tokens[$index];
348349
}
349350

350351
$visibilityToken = $tokens[$visibilityIndex];
351352
if ($tokens[$visibilityIndex]->isGivenKind(\T_VAR)) {
352353
$visibilityToken = new Token([\T_PUBLIC, 'public']);
353354
}
354-
$tokensToInsert[] = $visibilityToken;
355+
$tokensToInsertBefore[] = $visibilityToken;
356+
357+
$tokensToInsertAfter = [];
358+
if ($tokens[$removeTo]->isGivenKind(CT::T_PROPERTY_HOOK_BRACE_CLOSE)) {
359+
for ($index = $propertyIndex + 1; $index <= $removeTo; $index++) {
360+
$tokensToInsertAfter[] = $tokens[$index];
361+
}
362+
}
355363

356364
$tokens->clearRange($removeFrom + 1, $removeTo);
357365
TokenRemover::removeWithLinesIfPossible($tokens, $removeFrom);
358366

359-
return $tokensToInsert;
367+
return [$tokensToInsertBefore, $tokensToInsertAfter];
360368
}
361369

362370
/**
@@ -377,6 +385,10 @@ private static function getTokenOfKindSibling(Tokens $tokens, int $direction, in
377385
}
378386
}
379387

388+
if ($tokens[$index]->isGivenKind(CT::T_PROPERTY_HOOK_BRACE_CLOSE)) {
389+
break;
390+
}
391+
380392
$index += $direction;
381393
}
382394

@@ -412,31 +424,42 @@ private static function removeAssignment(Tokens $tokens, int $variableAssignment
412424
}
413425

414426
/**
415-
* @param list<Token> $tokensToInsert
427+
* @param list<Token> $tokensToInsertBefore
428+
* @param list<Token> $tokensToInsertAfter
416429
*/
417-
private function updateParameterSignature(Tokens $tokens, int $constructorParameterIndex, array $tokensToInsert, bool $makeTypeNullable): void
418-
{
430+
private function updateParameterSignature(
431+
Tokens $tokens,
432+
int $constructorParameterIndex,
433+
array $tokensToInsertBefore,
434+
array $tokensToInsertAfter,
435+
bool $makeTypeNullable
436+
): void {
419437
$prevElementIndex = $tokens->getPrevTokenOfKind($constructorParameterIndex, ['(', ',', [CT::T_ATTRIBUTE_CLOSE]]);
420438
\assert(\is_int($prevElementIndex));
421439

422440
$propertyStartIndex = $tokens->getNextMeaningfulToken($prevElementIndex);
423441
\assert(\is_int($propertyStartIndex));
424442

425-
foreach ($tokensToInsert as $index => $token) {
443+
foreach ($tokensToInsertBefore as $index => $token) {
426444
if ($token->isGivenKind(\T_PUBLIC)) {
427-
$tokensToInsert[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, $token->getContent()]);
445+
$tokensToInsertBefore[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, $token->getContent()]);
428446
} elseif ($token->isGivenKind(\T_PROTECTED)) {
429-
$tokensToInsert[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, $token->getContent()]);
447+
$tokensToInsertBefore[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, $token->getContent()]);
430448
} elseif ($token->isGivenKind(\T_PRIVATE)) {
431-
$tokensToInsert[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, $token->getContent()]);
449+
$tokensToInsertBefore[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, $token->getContent()]);
432450
}
433451
}
434-
$tokensToInsert[] = new Token([\T_WHITESPACE, ' ']);
452+
$tokensToInsertBefore[] = new Token([\T_WHITESPACE, ' ']);
435453

436454
if ($makeTypeNullable && !$tokens[$propertyStartIndex]->isGivenKind(CT::T_NULLABLE_TYPE)) {
437-
$tokensToInsert[] = new Token([CT::T_NULLABLE_TYPE, '?']);
455+
$tokensToInsertBefore[] = new Token([CT::T_NULLABLE_TYPE, '?']);
438456
}
439457

440-
$this->tokensToInsert[$propertyStartIndex] = $tokensToInsert;
458+
$this->tokensToInsert[$propertyStartIndex] = $tokensToInsertBefore;
459+
460+
$nextPropertyStartIndex = $tokens->getNextMeaningfulToken($propertyStartIndex);
461+
\assert(\is_int($nextPropertyStartIndex));
462+
463+
$this->tokensToInsert[$nextPropertyStartIndex + 1] = $tokensToInsertAfter;
441464
}
442465
}

tests/Fixer/PromotedConstructorPropertyFixerTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,4 +1067,41 @@ public function __construct(
10671067
];
10681068
}
10691069
}
1070+
1071+
/**
1072+
* @dataProvider provideFix84Cases
1073+
*
1074+
* @requires PHP >= 8.4
1075+
*/
1076+
public function testFix84(string $expected, ?string $input = null): void
1077+
{
1078+
$this->doTest($expected, $input);
1079+
}
1080+
1081+
/**
1082+
* @return iterable<array{0: string, 1?: string}>
1083+
*/
1084+
public static function provideFix84Cases(): iterable
1085+
{
1086+
yield 'promote property with hook' => [
1087+
<<<'PHP'
1088+
<?php class Foo {
1089+
public function __construct(
1090+
private string $bar { set (string $bar) { $this->bar = strtoupper($bar); } }
1091+
) {
1092+
}
1093+
}
1094+
PHP,
1095+
<<<'PHP'
1096+
<?php class Foo {
1097+
private string $bar { set (string $bar) { $this->bar = strtoupper($bar); } }
1098+
public function __construct(
1099+
string $bar
1100+
) {
1101+
$this->bar = $bar;
1102+
}
1103+
}
1104+
PHP,
1105+
];
1106+
}
10701107
}

0 commit comments

Comments
 (0)