Skip to content

Commit 27d0c63

Browse files
authored
ConstructorAnalyzer - allow finding all constructors (#649)
1 parent 64deda2 commit 27d0c63

File tree

5 files changed

+51
-22
lines changed

5 files changed

+51
-22
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![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)
44
[![PHP version](https://img.shields.io/packagist/php-v/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://php.net)
55
[![License](https://img.shields.io/github/license/kubawerlos/php-cs-fixer-custom-fixers.svg)](LICENSE)
6-
![Tests](https://img.shields.io/badge/tests-2995-brightgreen.svg)
6+
![Tests](https://img.shields.io/badge/tests-2996-brightgreen.svg)
77
[![Downloads](https://img.shields.io/packagist/dt/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers)
88

99
[![CI Status](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/actions)

src/Analyzer/ConstructorAnalyzer.php

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace PhpCsFixerCustomFixers\Analyzer;
1515

16+
use PhpCsFixer\Tokenizer\Token;
1617
use PhpCsFixer\Tokenizer\Tokens;
1718
use PhpCsFixer\Tokenizer\TokensAnalyzer;
1819
use PhpCsFixerCustomFixers\Analyzer\Analysis\ConstructorAnalysis;
@@ -22,36 +23,50 @@
2223
*/
2324
final class ConstructorAnalyzer
2425
{
25-
public function findNonAbstractConstructor(Tokens $tokens, int $classIndex): ?ConstructorAnalysis
26+
public function findConstructor(Tokens $tokens, int $classIndex, bool $allowAbstract): ?ConstructorAnalysis
2627
{
2728
if (!$tokens[$classIndex]->isGivenKind(\T_CLASS)) {
2829
throw new \InvalidArgumentException(\sprintf('Index %d is not a class.', $classIndex));
2930
}
3031

3132
$tokensAnalyzer = new TokensAnalyzer($tokens);
3233

33-
/** @var int $index */
34+
/**
35+
* @var int $index
36+
* @var array<string, int|string|Token> $element
37+
*/
3438
foreach ($tokensAnalyzer->getClassyElements() as $index => $element) {
3539
if ($element['classIndex'] !== $classIndex) {
3640
continue;
3741
}
38-
if ($element['type'] !== 'method') {
42+
43+
if (!$this->isConstructor($tokens, $index, $element)) {
3944
continue;
4045
}
4146

42-
/** @var int $functionNameIndex */
43-
$functionNameIndex = $tokens->getNextMeaningfulToken($index);
44-
45-
if ($tokens[$functionNameIndex]->equals([\T_STRING, '__construct'], false)) {
46-
$constructorData = $tokensAnalyzer->getMethodAttributes($index);
47-
if ($constructorData['abstract']) {
48-
return null;
49-
}
50-
51-
return new ConstructorAnalysis($tokens, $index);
47+
$constructorAttributes = $tokensAnalyzer->getMethodAttributes($index);
48+
if (!$allowAbstract && $constructorAttributes['abstract']) {
49+
return null;
5250
}
51+
52+
return new ConstructorAnalysis($tokens, $index);
5353
}
5454

5555
return null;
5656
}
57+
58+
/**
59+
* @param array<string, int|string|Token> $element
60+
*/
61+
private function isConstructor(Tokens $tokens, int $index, array $element): bool
62+
{
63+
if ($element['type'] !== 'method') {
64+
return false;
65+
}
66+
67+
/** @var int $functionNameIndex */
68+
$functionNameIndex = $tokens->getNextMeaningfulToken($index);
69+
70+
return $tokens[$functionNameIndex]->equals([\T_STRING, '__construct'], false);
71+
}
5772
}

src/Fixer/MultilinePromotedPropertiesFixer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public function fix(\SplFileInfo $file, Tokens $tokens): void
8282
continue;
8383
}
8484

85-
$constructorAnalysis = $constructorAnalyzer->findNonAbstractConstructor($tokens, $index);
85+
$constructorAnalysis = $constructorAnalyzer->findConstructor($tokens, $index, false);
8686
if ($constructorAnalysis === null) {
8787
continue;
8888
}

src/Fixer/PromotedConstructorPropertyFixer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public function fix(\SplFileInfo $file, Tokens $tokens): void
107107
continue;
108108
}
109109

110-
$constructorAnalysis = $constructorAnalyzer->findNonAbstractConstructor($tokens, $index);
110+
$constructorAnalysis = $constructorAnalyzer->findConstructor($tokens, $index, false);
111111
if ($constructorAnalysis === null) {
112112
continue;
113113
}

tests/Analyzer/ConstructorAnalyzerTest.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,29 @@
2525
*/
2626
final class ConstructorAnalyzerTest extends TestCase
2727
{
28-
public function testFindingNonAbstractConstructorWhenNotForClass(): void
28+
public function testFindingConstructorWhenNotForClass(): void
2929
{
3030
$this->expectException(\InvalidArgumentException::class);
3131
$this->expectExceptionMessage('Index 2 is not a class.');
3232

3333
$tokens = Tokens::fromCode('<?php $no . $class . $here;');
3434
$analyzer = new ConstructorAnalyzer();
3535

36-
$analyzer->findNonAbstractConstructor($tokens, 2);
36+
$analyzer->findConstructor($tokens, 2, true);
3737
}
3838

3939
/**
4040
* @param array<int, null|int> $expected
4141
*
4242
* @dataProvider provideFindingNonAbstractConstructorCases
4343
*/
44-
public function testFindingNonAbstractConstructor(array $expected, string $code): void
44+
public function testFindingNonAbstractConstructor(array $expected, bool $allowAbstract, string $code): void
4545
{
4646
$tokens = Tokens::fromCode($code);
4747
$analyzer = new ConstructorAnalyzer();
4848

4949
foreach ($expected as $classIndex => $nonAbstractConstructorIndex) {
50-
$constructorAnalysis = $analyzer->findNonAbstractConstructor($tokens, $classIndex);
50+
$constructorAnalysis = $analyzer->findConstructor($tokens, $classIndex, $allowAbstract);
5151

5252
if ($nonAbstractConstructorIndex === null) {
5353
self::assertNull($constructorAnalysis);
@@ -59,40 +59,53 @@ public function testFindingNonAbstractConstructor(array $expected, string $code)
5959
}
6060

6161
/**
62-
* @return iterable<array{array<int, null|int>, string}>
62+
* @return iterable<array{array<int, null|int>, bool, string}>
6363
*/
6464
public static function provideFindingNonAbstractConstructorCases(): iterable
6565
{
6666
yield 'no constructor' => [
6767
[3 => null],
68+
true,
6869
'<?php abstract class Foo {
6970
public function notConstructor() {}
7071
}',
7172
];
7273

73-
yield 'abstract constructor' => [
74+
yield 'abstract constructor allowed to be found' => [
75+
[3 => 13],
76+
true,
77+
'<?php abstract class Foo {
78+
abstract public function __construct() {}
79+
}',
80+
];
81+
82+
yield 'abstract constructor not allowed to be found' => [
7483
[3 => null],
84+
false,
7585
'<?php abstract class Foo {
7686
abstract public function __construct() {}
7787
}',
7888
];
7989

8090
yield 'public constructor' => [
8191
[3 => 11],
92+
true,
8293
'<?php abstract class Foo {
8394
public function __construct() {}
8495
}',
8596
];
8697

8798
yield 'uppercase constructor' => [
8899
[3 => 11],
100+
true,
89101
'<?php abstract class Foo {
90102
public function __CONSTRUCT() {}
91103
}',
92104
];
93105

94106
yield 'class with other elements' => [
95107
[3 => 29],
108+
true,
96109
'<?php abstract class Foo {
97110
public $a;
98111
public static function create() {}
@@ -103,6 +116,7 @@ public function bar() {}
103116

104117
yield 'multiple classes' => [
105118
[2 => 10, 21 => null, 29 => 37],
119+
true,
106120
'<?php
107121
class Foo {
108122
public function __construct() {}

0 commit comments

Comments
 (0)