Skip to content

Commit b4313a7

Browse files
committed
PHP 8.0 | Generic/LowerCaseType: refactor [3] - allow for union types
Allow for union types, as introduced in PHP 8.0, in return and parameter type declarations. Includes unit tests.
1 parent 4c92103 commit b4313a7

File tree

4 files changed

+95
-13
lines changed

4 files changed

+95
-13
lines changed

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

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,23 @@ public function process(File $phpcsFile, $stackPtr)
8888
$props = $phpcsFile->getMethodProperties($stackPtr);
8989

9090
// Strip off potential nullable indication.
91-
$returnType = ltrim($props['return_type'], '?');
92-
$returnTypeLower = strtolower($returnType);
91+
$returnType = ltrim($props['return_type'], '?');
9392

94-
if ($returnType !== ''
95-
&& isset($this->phpTypes[$returnTypeLower]) === true
96-
) {
93+
if ($returnType !== '') {
9794
$error = 'PHP return type declarations must be lowercase; expected "%s" but found "%s"';
9895
$errorCode = 'ReturnTypeFound';
9996

100-
$this->processType($phpcsFile, $props['return_type_token'], $returnType, $error, $errorCode);
97+
if (strpos($returnType, '|') !== false) {
98+
$this->processUnionType(
99+
$phpcsFile,
100+
$props['return_type_token'],
101+
$props['return_type_end_token'],
102+
$error,
103+
$errorCode
104+
);
105+
} else if (isset($this->phpTypes[strtolower($returnType)]) === true) {
106+
$this->processType($phpcsFile, $props['return_type_token'], $returnType, $error, $errorCode);
107+
}
101108
}
102109

103110
/*
@@ -111,22 +118,77 @@ public function process(File $phpcsFile, $stackPtr)
111118

112119
foreach ($params as $param) {
113120
// Strip off potential nullable indication.
114-
$typeHint = ltrim($param['type_hint'], '?');
115-
$typeHintLower = strtolower($typeHint);
121+
$typeHint = ltrim($param['type_hint'], '?');
116122

117-
if ($typeHint !== ''
118-
&& isset($this->phpTypes[$typeHintLower]) === true
119-
) {
123+
if ($typeHint !== '') {
120124
$error = 'PHP parameter type declarations must be lowercase; expected "%s" but found "%s"';
121125
$errorCode = 'ParamTypeFound';
122126

123-
$this->processType($phpcsFile, $param['type_hint_token'], $typeHint, $error, $errorCode);
127+
if (strpos($typeHint, '|') !== false) {
128+
$this->processUnionType(
129+
$phpcsFile,
130+
$param['type_hint_token'],
131+
$param['type_hint_end_token'],
132+
$error,
133+
$errorCode
134+
);
135+
} else if (isset($this->phpTypes[strtolower($typeHint)]) === true) {
136+
$this->processType($phpcsFile, $param['type_hint_token'], $typeHint, $error, $errorCode);
137+
}
124138
}
125-
}
139+
}//end foreach
126140

127141
}//end process()
128142

129143

144+
/**
145+
* Processes a union type declaration.
146+
*
147+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
148+
* @param int $typeDeclStart The position of the start of the type token.
149+
* @param int $typeDeclEnd The position of the end of the type token.
150+
* @param string $error Error message template.
151+
* @param string $errorCode The error code.
152+
*
153+
* @return void
154+
*/
155+
protected function processUnionType(File $phpcsFile, $typeDeclStart, $typeDeclEnd, $error, $errorCode)
156+
{
157+
$tokens = $phpcsFile->getTokens();
158+
$current = $typeDeclStart;
159+
160+
do {
161+
$endOfType = $phpcsFile->findNext(T_TYPE_UNION, $current, $typeDeclEnd);
162+
if ($endOfType === false) {
163+
// This must be the last type in the union.
164+
$endOfType = ($typeDeclEnd + 1);
165+
}
166+
167+
$hasNsSep = $phpcsFile->findNext(T_NS_SEPARATOR, $current, $endOfType);
168+
if ($hasNsSep !== false) {
169+
// Multi-token class based type. Ignore.
170+
$current = ($endOfType + 1);
171+
continue;
172+
}
173+
174+
// Type consisting of a single token.
175+
$startOfType = $phpcsFile->findNext(Tokens::$emptyTokens, $current, $endOfType, true);
176+
if ($startOfType === false) {
177+
// Parse error.
178+
return;
179+
}
180+
181+
$type = $tokens[$startOfType]['content'];
182+
if (isset($this->phpTypes[strtolower($type)]) === true) {
183+
$this->processType($phpcsFile, $startOfType, $type, $error, $errorCode);
184+
}
185+
186+
$current = ($endOfType + 1);
187+
} while ($current <= $typeDeclEnd);
188+
189+
}//end processUnionType()
190+
191+
130192
/**
131193
* Processes a type cast or a singular type declaration.
132194
*

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,11 @@ $foo = function (?Int $a, ? Callable $b)
4545

4646
$var = (BInARY) $string;
4747
$var = (binary)$string;
48+
49+
function unionParamTypesA (bool|array| /* nullability operator not allowed in union */ NULL $var) {}
50+
51+
function unionParamTypesB (\Package\ClassName | Int | \Package\Other_Class | FALSE $var) {}
52+
53+
function unionReturnTypesA ($var): bool|array| /* nullability operator not allowed in union */ NULL {}
54+
55+
function unionReturnTypesB ($var): \Package\ClassName | Int | \Package\Other_Class | FALSE {}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,11 @@ $foo = function (?int $a, ? callable $b)
4545

4646
$var = (binary) $string;
4747
$var = (binary)$string;
48+
49+
function unionParamTypesA (bool|array| /* nullability operator not allowed in union */ null $var) {}
50+
51+
function unionParamTypesB (\Package\ClassName | int | \Package\Other_Class | false $var) {}
52+
53+
function unionReturnTypesA ($var): bool|array| /* nullability operator not allowed in union */ null {}
54+
55+
function unionReturnTypesB ($var): \Package\ClassName | int | \Package\Other_Class | false {}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ public function getErrorList()
4545
43 => 2,
4646
44 => 1,
4747
46 => 1,
48+
49 => 1,
49+
51 => 2,
50+
53 => 1,
51+
55 => 2,
4852
];
4953

5054
}//end getErrorList()

0 commit comments

Comments
 (0)