Skip to content

Commit 5e9339a

Browse files
authored
Merge pull request #331 from PHPCSStandards/php-8.3/generic-lowercasetype-support-typed-constants
PHP 8.3 | Generic/LowerCaseType: add support for typed constants
2 parents 1969478 + b265092 commit 5e9339a

File tree

4 files changed

+181
-47
lines changed

4 files changed

+181
-47
lines changed

src/Standards/Generic/Sniffs/PHP/LowerCaseTypeSniff.php

+64-4
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function process(File $phpcsFile, $stackPtr)
8787
}
8888

8989
/*
90-
* Check property types.
90+
* Check OO constant and property types.
9191
*/
9292

9393
if (isset(Tokens::$ooScopeTokens[$tokens[$stackPtr]['code']]) === true) {
@@ -97,22 +97,82 @@ public function process(File $phpcsFile, $stackPtr)
9797

9898
for ($i = ($tokens[$stackPtr]['scope_opener'] + 1); $i < $tokens[$stackPtr]['scope_closer']; $i++) {
9999
// Skip over potentially large docblocks.
100-
if ($tokens[$i]['code'] === \T_DOC_COMMENT_OPEN_TAG
100+
if ($tokens[$i]['code'] === T_DOC_COMMENT_OPEN_TAG
101101
&& isset($tokens[$i]['comment_closer']) === true
102102
) {
103103
$i = $tokens[$i]['comment_closer'];
104104
continue;
105105
}
106106

107107
// Skip over function declarations and everything nested within.
108-
if ($tokens[$i]['code'] === \T_FUNCTION
108+
if ($tokens[$i]['code'] === T_FUNCTION
109109
&& isset($tokens[$i]['scope_closer']) === true
110110
) {
111111
$i = $tokens[$i]['scope_closer'];
112112
continue;
113113
}
114114

115-
if ($tokens[$i]['code'] !== \T_VARIABLE) {
115+
if ($tokens[$i]['code'] === T_CONST) {
116+
$ignore = Tokens::$emptyTokens;
117+
$ignore[T_NULLABLE] = T_NULLABLE;
118+
119+
$startOfType = $phpcsFile->findNext($ignore, ($i + 1), null, true);
120+
if ($startOfType === false) {
121+
// Parse error/live coding. Nothing to do. Rest of loop is moot.
122+
return;
123+
}
124+
125+
$assignmentOperator = $phpcsFile->findNext([T_EQUAL, T_SEMICOLON], ($startOfType + 1));
126+
if ($assignmentOperator === false || $tokens[$assignmentOperator]['code'] !== T_EQUAL) {
127+
// Parse error/live coding. Nothing to do. Rest of loop is moot.
128+
return;
129+
}
130+
131+
$constName = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($assignmentOperator - 1), null, true);
132+
if ($startOfType !== $constName) {
133+
$endOfType = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($constName - 1), null, true);
134+
135+
$type = '';
136+
$isUnionType = false;
137+
$isIntersectionType = false;
138+
for ($j = $startOfType; $j <= $endOfType; $j++) {
139+
if (isset($ignore[$tokens[$j]['code']]) === true) {
140+
continue;
141+
}
142+
143+
if ($tokens[$j]['code'] === T_TYPE_UNION) {
144+
$isUnionType = true;
145+
}
146+
147+
if ($tokens[$j]['code'] === T_TYPE_INTERSECTION) {
148+
$isIntersectionType = true;
149+
}
150+
151+
$type .= $tokens[$j]['content'];
152+
}
153+
154+
$error = 'PHP constant type declarations must be lowercase; expected "%s" but found "%s"';
155+
$errorCode = 'ConstantTypeFound';
156+
157+
if ($isIntersectionType === true) {
158+
// Intersection types don't support simple types.
159+
} else if ($isUnionType === true) {
160+
$this->processUnionType(
161+
$phpcsFile,
162+
$startOfType,
163+
$endOfType,
164+
$error,
165+
$errorCode
166+
);
167+
} else if (isset($this->phpTypes[strtolower($type)]) === true) {
168+
$this->processType($phpcsFile, $startOfType, $type, $error, $errorCode);
169+
}
170+
}//end if
171+
172+
continue;
173+
}//end if
174+
175+
if ($tokens[$i]['code'] !== T_VARIABLE) {
116176
continue;
117177
}
118178

src/Standards/Generic/Tests/PHP/LowerCaseTypeUnitTest.inc

+30
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,36 @@ $arrow = fn (Int $a, String $b, BOOL $c, Array $d, Foo\Bar $e) : Float => $a * $
9595

9696
$cl = function (False $a, TRUE $b, Null $c): ?True {};
9797

98+
class TypedClassConstants
99+
{
100+
const UNTYPED = null;
101+
const FLOAT = 'Reserved keyword as name is valid and should not be changed';
102+
const OBJECT = 'Reserved keyword as name is valid and should not be changed';
103+
104+
const ClassName FIRST = null;
105+
public const Int SECOND = 0;
106+
private const ?BOOL THIRD = false;
107+
public const Self FOURTH = null;
108+
}
109+
interface TypedInterfaceConstants
110+
{
111+
protected const PaRenT FIRST = null;
112+
private const ARRAY SECOND = [];
113+
public const Float THIRD = 2.5;
114+
final const ?STRING FOURTH = 'fourth';
115+
}
116+
trait TypedTraitConstants {
117+
const IterablE FIRST = null;
118+
const Object SECOND = null;
119+
const Mixed THIRD = 'third';
120+
}
121+
enum TypedEnumConstants {
122+
public const Iterable|FALSE|NULL FIRST = null;
123+
protected const SELF|Parent /* comment */ |\Fully\Qualified\ClassName|UnQualifiedClass SECOND = null;
124+
private const ClassName|/*comment*/Float|STRING|False THIRD = 'third';
125+
public const sTRing | aRRaY | FaLSe FOURTH = 'fourth';
126+
}
127+
98128
// Intentional error, should be ignored by the sniff.
99129
interface PropertiesNotAllowed {
100130
public $notAllowed;

src/Standards/Generic/Tests/PHP/LowerCaseTypeUnitTest.inc.fixed

+30
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,36 @@ $arrow = fn (int $a, string $b, bool $c, array $d, Foo\Bar $e) : float => $a * $
9595

9696
$cl = function (false $a, true $b, null $c): ?true {};
9797

98+
class TypedClassConstants
99+
{
100+
const UNTYPED = null;
101+
const FLOAT = 'Reserved keyword as name is valid and should not be changed';
102+
const OBJECT = 'Reserved keyword as name is valid and should not be changed';
103+
104+
const ClassName FIRST = null;
105+
public const int SECOND = 0;
106+
private const ?bool THIRD = false;
107+
public const self FOURTH = null;
108+
}
109+
interface TypedInterfaceConstants
110+
{
111+
protected const parent FIRST = null;
112+
private const array SECOND = [];
113+
public const float THIRD = 2.5;
114+
final const ?string FOURTH = 'fourth';
115+
}
116+
trait TypedTraitConstants {
117+
const iterable FIRST = null;
118+
const object SECOND = null;
119+
const mixed THIRD = 'third';
120+
}
121+
enum TypedEnumConstants {
122+
public const iterable|false|null FIRST = null;
123+
protected const self|parent /* comment */ |\Fully\Qualified\ClassName|UnQualifiedClass SECOND = null;
124+
private const ClassName|/*comment*/float|string|false THIRD = 'third';
125+
public const string | array | false FOURTH = 'fourth';
126+
}
127+
98128
// Intentional error, should be ignored by the sniff.
99129
interface PropertiesNotAllowed {
100130
public $notAllowed;

src/Standards/Generic/Tests/PHP/LowerCaseTypeUnitTest.php

+57-43
Original file line numberDiff line numberDiff line change
@@ -31,48 +31,62 @@ final class LowerCaseTypeUnitTest extends AbstractSniffUnitTest
3131
public function getErrorList()
3232
{
3333
return [
34-
14 => 1,
35-
15 => 1,
36-
16 => 1,
37-
17 => 1,
38-
18 => 1,
39-
21 => 4,
40-
22 => 3,
41-
23 => 3,
42-
25 => 1,
43-
26 => 2,
44-
27 => 2,
45-
32 => 4,
46-
36 => 1,
47-
37 => 1,
48-
38 => 1,
49-
39 => 1,
50-
43 => 2,
51-
44 => 1,
52-
46 => 1,
53-
49 => 1,
54-
51 => 2,
55-
53 => 1,
56-
55 => 2,
57-
60 => 1,
58-
61 => 1,
59-
62 => 1,
60-
63 => 1,
61-
64 => 1,
62-
65 => 1,
63-
66 => 1,
64-
67 => 1,
65-
68 => 1,
66-
69 => 1,
67-
71 => 3,
68-
72 => 2,
69-
73 => 3,
70-
74 => 3,
71-
78 => 3,
72-
82 => 2,
73-
85 => 1,
74-
94 => 5,
75-
96 => 4,
34+
14 => 1,
35+
15 => 1,
36+
16 => 1,
37+
17 => 1,
38+
18 => 1,
39+
21 => 4,
40+
22 => 3,
41+
23 => 3,
42+
25 => 1,
43+
26 => 2,
44+
27 => 2,
45+
32 => 4,
46+
36 => 1,
47+
37 => 1,
48+
38 => 1,
49+
39 => 1,
50+
43 => 2,
51+
44 => 1,
52+
46 => 1,
53+
49 => 1,
54+
51 => 2,
55+
53 => 1,
56+
55 => 2,
57+
60 => 1,
58+
61 => 1,
59+
62 => 1,
60+
63 => 1,
61+
64 => 1,
62+
65 => 1,
63+
66 => 1,
64+
67 => 1,
65+
68 => 1,
66+
69 => 1,
67+
71 => 3,
68+
72 => 2,
69+
73 => 3,
70+
74 => 3,
71+
78 => 3,
72+
82 => 2,
73+
85 => 1,
74+
94 => 5,
75+
96 => 4,
76+
105 => 1,
77+
106 => 1,
78+
107 => 1,
79+
111 => 1,
80+
112 => 1,
81+
113 => 1,
82+
114 => 1,
83+
117 => 1,
84+
118 => 1,
85+
119 => 1,
86+
122 => 3,
87+
123 => 2,
88+
124 => 3,
89+
125 => 3,
7690
];
7791

7892
}//end getErrorList()
@@ -89,7 +103,7 @@ public function getErrorList()
89103
public function getWarningList()
90104
{
91105
// Warning from getMemberProperties() about parse error.
92-
return [100 => 1];
106+
return [130 => 1];
93107

94108
}//end getWarningList()
95109

0 commit comments

Comments
 (0)