Skip to content

Commit 5ff8a0e

Browse files
committed
Update
1 parent 5e98279 commit 5ff8a0e

File tree

3 files changed

+86
-40
lines changed

3 files changed

+86
-40
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-3729-brightgreen.svg)
8+
![Tests](https://img.shields.io/badge/tests-3733-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/TypedClassConstantFixer.php

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,6 @@
2121

2222
final class TypedClassConstantFixer extends AbstractFixer
2323
{
24-
private const TOKEN_TO_TYPE_MAP = [
25-
\T_DNUMBER => 'float',
26-
\T_LNUMBER => 'int',
27-
\T_CONSTANT_ENCAPSED_STRING => 'string',
28-
CT::T_ARRAY_SQUARE_BRACE_CLOSE => 'array',
29-
];
30-
3124
public function getDefinition(): FixerDefinitionInterface
3225
{
3326
return new FixerDefinition(
@@ -110,38 +103,72 @@ private static function fixClass(Tokens $tokens, int $openParenthesisIndex, int
110103
}
111104
}
112105

113-
private static function getTypeOfExpression(Tokens $tokens, int $assignmentIndex): string
106+
private static function getTypeOfExpression(Tokens $tokens, int $index): string
114107
{
115-
$semicolonIndex = $tokens->getNextTokenOfKind($assignmentIndex, [';']);
108+
$semicolonIndex = $tokens->getNextTokenOfKind($index, [';']);
116109
\assert(\is_int($semicolonIndex));
117110

118111
$beforeSemicolonIndex = $tokens->getPrevMeaningfulToken($semicolonIndex);
119112
\assert(\is_int($beforeSemicolonIndex));
120113

121-
$tokenId = $tokens[$beforeSemicolonIndex]->getId();
114+
$foundKinds = [];
115+
116+
$index = $tokens->getNextMeaningfulToken($index);
117+
\assert(\is_int($index));
122118

123-
if (isset(self::TOKEN_TO_TYPE_MAP[$tokenId])) {
124-
return self::TOKEN_TO_TYPE_MAP[$tokenId];
119+
if ($tokens[$index]->isGivenKind([\T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) {
120+
return 'array';
125121
}
126122

127-
if ($tokens[$beforeSemicolonIndex]->isGivenKind(\T_STRING)) {
123+
do {
124+
$foundKinds[] = $tokens[$index]->getId() ?? $tokens[$index]->getContent();
125+
126+
$index = $tokens->getNextMeaningfulToken($index);
127+
\assert(\is_int($index));
128+
} while ($index < $semicolonIndex);
129+
130+
if ($foundKinds === [\T_STRING]) {
128131
$lowercasedContent = \strtolower($tokens[$beforeSemicolonIndex]->getContent());
129132
if (\in_array($lowercasedContent, ['false', 'true', 'null'], true)) {
130133
return $lowercasedContent;
131134
}
132135
}
133136

134-
if ($tokens[$beforeSemicolonIndex]->equals(')')) {
135-
$openParenthesisIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $beforeSemicolonIndex);
137+
return self::getTypeOfExpressionForTokenKinds($foundKinds);
138+
}
136139

137-
$arrayIndex = $tokens->getPrevMeaningfulToken($openParenthesisIndex);
138-
\assert(\is_int($arrayIndex));
140+
/**
141+
* @param list<int|string> $tokenKinds
142+
*/
143+
private static function getTypeOfExpressionForTokenKinds(array $tokenKinds): string
144+
{
145+
if (self::hasExclusivelyKinds($tokenKinds, [\T_LNUMBER, '+', '-', '*', '(', ')'])) {
146+
return 'int';
147+
}
139148

140-
if ($tokens[$arrayIndex]->isGivenKind(\T_ARRAY)) {
141-
return 'array';
142-
}
149+
if (self::hasExclusivelyKinds($tokenKinds, [\T_DNUMBER, \T_LNUMBER, '+', '-', '*', '/', '(', ')'])) {
150+
return 'float';
151+
}
152+
153+
if (self::hasExclusivelyKinds($tokenKinds, [\T_CONSTANT_ENCAPSED_STRING, '.', \T_LNUMBER, \T_DNUMBER])) {
154+
return 'string';
143155
}
144156

145157
return 'mixed';
146158
}
159+
160+
/**
161+
* @param list<int|string> $tokenKinds
162+
* @param list<int|string> $expectedKinds
163+
*/
164+
private static function hasExclusivelyKinds(array $tokenKinds, array $expectedKinds): bool
165+
{
166+
foreach ($tokenKinds as $index => $tokenKind) {
167+
if (\in_array($tokenKind, $expectedKinds, true)) {
168+
unset($tokenKinds[$index]);
169+
}
170+
}
171+
172+
return $tokenKinds === [];
173+
}
147174
}

tests/Fixer/TypedClassConstantFixerTest.php

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public static function provideFixCases(): iterable
5050
'<?php class Foo { public const BAR = [true, 1, "foo"]; }',
5151
];
5252

53+
yield 'array as result of an expression' => [
54+
'<?php class Foo { public const array BAR = [1 => 2] + array(3 => 4) + [5 => 6]; }',
55+
'<?php class Foo { public const BAR = [1 => 2] + array(3 => 4) + [5 => 6]; }',
56+
];
57+
5358
yield 'false' => [
5459
'<?php class Foo { public const false BAR = false; }',
5560
'<?php class Foo { public const BAR = false; }',
@@ -60,19 +65,29 @@ public static function provideFixCases(): iterable
6065
'<?php class Foo { public const BAR = true; }',
6166
];
6267

63-
yield 'float' => [
64-
'<?php class Foo { public const float BAR = 2.5; }',
65-
'<?php class Foo { public const BAR = 2.5; }',
66-
];
67-
6868
yield 'integer' => [
6969
'<?php class Foo { public const int BAR = 123; }',
7070
'<?php class Foo { public const BAR = 123; }',
7171
];
7272

73-
yield 'integer as result of sum' => [
74-
'<?php class Foo { public const int BAR = 1 + 2 + 3; }',
75-
'<?php class Foo { public const BAR = 1 + 2 + 3; }',
73+
yield 'integer as result of an expression' => [
74+
'<?php class Foo { public const int BAR = 1 + 2 - 3 * 4; }',
75+
'<?php class Foo { public const BAR = 1 + 2 - 3 * 4; }',
76+
];
77+
78+
yield 'integer as result of an expression with parentheses' => [
79+
'<?php class Foo { public const int BAR = 1000 * (701 + 22); }',
80+
'<?php class Foo { public const BAR = 1000 * (701 + 22); }',
81+
];
82+
83+
yield 'float' => [
84+
'<?php class Foo { public const float BAR = 2.5; }',
85+
'<?php class Foo { public const BAR = 2.5; }',
86+
];
87+
88+
yield 'float as result of expression' => [
89+
'<?php class Foo { public const float BAR = 1 + (2 - 3) * 4 / 5; }',
90+
'<?php class Foo { public const BAR = 1 + (2 - 3) * 4 / 5; }',
7691
];
7792

7893
yield 'null' => [
@@ -95,14 +110,9 @@ public static function provideFixCases(): iterable
95110
"<?php class Foo { public const BAR = 'John Doe'; }",
96111
];
97112

98-
yield 'unknown other constant' => [
99-
'<?php class Foo { public const mixed BAR = CONSTANT_FROM_FAR_AWAY; }',
100-
'<?php class Foo { public const BAR = CONSTANT_FROM_FAR_AWAY; }',
101-
];
102-
103-
yield 'expression of unknown type' => [
104-
'<?php class Foo { public const mixed BAR = 10 * (FLOAT_OR_INTEGER + 7); }',
105-
'<?php class Foo { public const BAR = 10 * (FLOAT_OR_INTEGER + 7); }',
113+
yield 'string as result of concatenation' => [
114+
'<?php class Foo { public const string BAR = "A" . 1 . "B" . 0.25 . "C"; }',
115+
'<?php class Foo { public const BAR = "A" . 1 . "B" . 0.25 . "C"; }',
106116
];
107117

108118
yield 'multiple constants' => [
@@ -136,9 +146,18 @@ class Bar {
136146
PHP,
137147
];
138148

139-
// if someone does these, they deserve to have their code broken
149+
yield 'unknown other constant' => [
150+
'<?php class Foo { public const mixed BAR = CONSTANT_FROM_FAR_AWAY; }',
151+
'<?php class Foo { public const BAR = CONSTANT_FROM_FAR_AWAY; }',
152+
];
153+
154+
yield 'expression of unknown type' => [
155+
'<?php class Foo { public const mixed BAR = 10 * FLOAT_OR_INTEGER + 3; }',
156+
'<?php class Foo { public const BAR = 10 * FLOAT_OR_INTEGER + 3; }',
157+
];
158+
140159
yield 'constant that can be of different types' => [
141-
'<?php class Foo { public const string BAR = SHOULD_BE_INT ? 1 : "one"; }',
160+
'<?php class Foo { public const mixed BAR = SHOULD_BE_INT ? 1 : "one"; }',
142161
'<?php class Foo { public const BAR = SHOULD_BE_INT ? 1 : "one"; }',
143162
];
144163

@@ -147,7 +166,7 @@ class Bar {
147166
<?php
148167
class HellCoreServiceManagerHelper
149168
{
150-
const float OPTION_666__YES__1010011010_VALUE_4_1_3
169+
const mixed OPTION_666__YES__1010011010_VALUE_4_1_3
151170
= IS_OVERRIDEN_BY_BEELZEBOSS
152171
? "Hell yeah"
153172
: CIRCLES_MANAGER_ACCESS === [0o1232, 'super_manager', false, -66.6]

0 commit comments

Comments
 (0)