Skip to content

Commit f8e6fe9

Browse files
committed
Fix docblocks to allow iterable and () grouping.
1 parent bf99bd2 commit f8e6fe9

File tree

1 file changed

+82
-31
lines changed

1 file changed

+82
-31
lines changed

PSR2R/Sniffs/Commenting/FullyQualifiedClassNameInDocBlockSniff.php

Lines changed: 82 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class FullyQualifiedClassNameInDocBlockSniff extends AbstractSniff {
1818
*/
1919
public static $whitelistedTypes = [
2020
'string', 'int', 'integer', 'float', 'bool', 'boolean', 'resource', 'null', 'void', 'callable',
21-
'array', 'mixed', 'object', 'false', 'true', 'self', 'static', '$this',
21+
'array', 'iterable', 'mixed', 'object', 'false', 'true', 'self', 'static', '$this',
2222
];
2323

2424
/**
@@ -76,15 +76,16 @@ public function process(File $phpCsFile, $stackPointer) {
7676
continue;
7777
}
7878

79-
$classNames = explode('|', $content);
80-
$this->fixClassNames($phpCsFile, $classNameIndex, $classNames, $appendix);
79+
$types = $this->parseTypes($content);
80+
81+
$this->fixClassNames($phpCsFile, $classNameIndex, $types, $appendix);
8182
}
8283
}
8384

8485
/**
8586
* @param \PHP_CodeSniffer\Files\File $phpCsFile
8687
* @param int $classNameIndex
87-
* @param array $classNames
88+
* @param string[] $classNames
8889
* @param string $appendix
8990
*
9091
* @return void
@@ -113,20 +114,35 @@ protected function fixClassNames(File $phpCsFile, $classNameIndex, array $classN
113114
* @param int $classNameIndex
114115
* @param string[] $classNames
115116
*
116-
* @return array
117+
* @return string[]
117118
*/
118119
protected function generateClassNameMap(File $phpCsFile, $classNameIndex, array &$classNames) {
119120
$result = [];
120121

121122
foreach ($classNames as $key => $className) {
122-
if (strpos($className, '\\') !== false) {
123-
continue;
124-
}
125123
$arrayOfObject = 0;
126124
while (substr($className, -2) === '[]') {
127125
$arrayOfObject++;
128126
$className = substr($className, 0, -2);
129127
}
128+
129+
if (preg_match('#^\((.+)\)#', $className, $matches)) {
130+
$subClassNames = explode('|', $matches[1]);
131+
$newClassName = '(' . $this->generateClassNameMapForUnionType($phpCsFile, $classNameIndex, $className, $subClassNames) . ')';
132+
if ($newClassName === $className) {
133+
continue;
134+
}
135+
136+
$classNames[$key] = $newClassName . ($arrayOfObject ? str_repeat('[]', $arrayOfObject) : '');
137+
$result[$className . ($arrayOfObject ? str_repeat('[]', $arrayOfObject) : '')] = $classNames[$key];
138+
139+
continue;
140+
}
141+
142+
if (strpos($className, '\\') !== false) {
143+
continue;
144+
}
145+
130146
if (in_array($className, static::$whitelistedTypes, true)) {
131147
continue;
132148
}
@@ -226,30 +242,8 @@ protected function getNamespace(File $phpCsFile) {
226242

227243
/**
228244
* @param \PHP_CodeSniffer\Files\File $phpCsFile
229-
* @param int $stackPointer
230245
*
231-
* @return int|null Stackpointer value of docblock end tag, or null if cannot be found
232-
*/
233-
protected function findRelatedDocBlock(File $phpCsFile, $stackPointer) {
234-
$tokens = $phpCsFile->getTokens();
235-
236-
$line = $tokens[$stackPointer]['line'];
237-
$beginningOfLine = $stackPointer;
238-
while (!empty($tokens[$beginningOfLine - 1]) && $tokens[$beginningOfLine - 1]['line'] === $line) {
239-
$beginningOfLine--;
240-
}
241-
242-
if (!empty($tokens[$beginningOfLine - 2]) && $tokens[$beginningOfLine - 2]['type'] === 'T_DOC_COMMENT_CLOSE_TAG') {
243-
return $beginningOfLine - 2;
244-
}
245-
246-
return null;
247-
}
248-
249-
/**
250-
* @param \PHP_CodeSniffer\Files\File $phpCsFile
251-
*
252-
* @return array
246+
* @return string[]
253247
*/
254248
protected function parseUseStatements(File $phpCsFile) {
255249
$useStatements = [];
@@ -286,4 +280,61 @@ protected function parseUseStatements(File $phpCsFile) {
286280
return $useStatements;
287281
}
288282

283+
/**
284+
* @param string $content
285+
*
286+
* @return string[]
287+
*/
288+
protected function parseTypes($content) {
289+
preg_match_all('#\(.+\)#', $content, $matches);
290+
if (!$matches[0]) {
291+
return explode('|', $content);
292+
}
293+
$unionTypes = $matches[0];
294+
$map = [];
295+
foreach ($unionTypes as $i => $unionType) {
296+
$content = str_replace($unionType, '{{t' . $i . '}}', $content);
297+
$map['{{t' . $i . '}}'] = $unionType;
298+
}
299+
300+
$types = explode('|', $content);
301+
foreach ($types as $k => $type) {
302+
$types[$k] = str_replace(array_keys($map), array_values($map), $type);
303+
}
304+
305+
return $types;
306+
}
307+
308+
/**
309+
* @param \PHP_CodeSniffer\Files\File $phpCsFile
310+
* @param int $classNameIndex
311+
* @param string $className
312+
* @param string[] $subClassNames
313+
*
314+
* @return string
315+
*/
316+
protected function generateClassNameMapForUnionType(File $phpCsFile, $classNameIndex, $className, array $subClassNames) {
317+
foreach ($subClassNames as $i => $subClassName) {
318+
if (strpos($subClassName, '\\') !== false) {
319+
continue;
320+
}
321+
322+
if (in_array($subClassName, static::$whitelistedTypes, true)) {
323+
continue;
324+
}
325+
$useStatement = $this->findUseStatementForClassName($phpCsFile, $subClassName);
326+
if (!$useStatement) {
327+
$message = 'Invalid typehint `%s`';
328+
if (substr($subClassName, 0, 1) === '$') {
329+
$message = 'The typehint seems to be missing for `%s`';
330+
}
331+
$phpCsFile->addError(sprintf($message, $subClassName), $classNameIndex, 'ClassNameInvalid');
332+
continue;
333+
}
334+
$subClassNames[$i] = $useStatement;
335+
}
336+
337+
return implode('|', $subClassNames);
338+
}
339+
289340
}

0 commit comments

Comments
 (0)