Skip to content

Commit fb181c8

Browse files
authored
"squizlabs/php_codesniffer": "^4.0.0" (#34)
* "squizlabs/php_codesniffer": "^4.0.0" * Update phpcsstandards/phpcsextra version requirement * Fix FQCN false positive issue. * Fix array bracket issue with comments. * Fix indentation issue. * Change minimum stability from 'dev' to 'stable'
1 parent ffe2a14 commit fb181c8

File tree

20 files changed

+323
-85
lines changed

20 files changed

+323
-85
lines changed

PhpCollective/Sniffs/Arrays/ArrayBracketSpacingSniff.php

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ public function process(File $phpcsFile, $stackPtr): void
7070
// Any extra blank lines should be removed
7171
if ($closerLine - $lastContentLine > 1) {
7272
$error = 'Extra blank lines found before array closing bracket';
73+
74+
// Check if the last content is a comment - these are problematic for auto-fixing
75+
if ($tokens[$lastContentPtr]['code'] === T_COMMENT) {
76+
// Report as non-fixable error when last element is a comment
77+
$phpcsFile->addError($error, $closerPtr, 'ExtraBlankLineBeforeCloser');
78+
79+
return;
80+
}
81+
7382
$fix = $phpcsFile->addFixableError($error, $closerPtr, 'ExtraBlankLineBeforeCloser');
7483

7584
if ($fix === true) {
@@ -90,13 +99,33 @@ public function process(File $phpcsFile, $stackPtr): void
9099
}
91100
}
92101

93-
// Remove all tokens between last content and closer
102+
// Find whitespace tokens between last content and closer
103+
$whitespaceTokens = [];
94104
for ($i = $lastContentPtr + 1; $i < $closerPtr; $i++) {
95-
$phpcsFile->fixer->replaceToken($i, '');
105+
if ($tokens[$i]['code'] === T_WHITESPACE) {
106+
$whitespaceTokens[] = $i;
107+
}
96108
}
97109

98-
// Add a single newline with proper indentation after the last content
99-
$phpcsFile->fixer->addContent($lastContentPtr, "\n" . $indent);
110+
// Find the position to insert the newline
111+
// If there's already whitespace, replace it; otherwise add new
112+
$nextToken = $lastContentPtr + 1;
113+
114+
if ($nextToken < $closerPtr && $tokens[$nextToken]['code'] === T_WHITESPACE) {
115+
// There's whitespace right after the last content
116+
// Replace it with a single newline and indent
117+
$phpcsFile->fixer->replaceToken($nextToken, "\n" . $indent);
118+
119+
// Remove any additional whitespace tokens
120+
for ($i = $nextToken + 1; $i < $closerPtr; $i++) {
121+
if ($tokens[$i]['code'] === T_WHITESPACE) {
122+
$phpcsFile->fixer->replaceToken($i, '');
123+
}
124+
}
125+
} else {
126+
// No whitespace immediately after, need to add it
127+
$phpcsFile->fixer->addContent($lastContentPtr, "\n" . $indent);
128+
}
100129

101130
$phpcsFile->fixer->endChangeset();
102131
}

PhpCollective/Sniffs/Classes/MethodDeclarationSniff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
4040
$tokens = $phpcsFile->getTokens();
4141

4242
$methodName = $phpcsFile->getDeclarationName($stackPtr);
43-
if ($methodName === null) {
43+
if (!$methodName) {
4444
// Ignore closures.
4545
return;
4646
}

PhpCollective/Sniffs/Classes/MethodTypeHintSniff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function process(File $phpcsFile, $stackPtr): void
5050
$j = $startIndex;
5151
$extractedUseStatement = '';
5252
while (true) {
53-
if (!$this->isGivenKind([T_NS_SEPARATOR, T_STRING, T_RETURN_TYPE], $tokens[$j])) {
53+
if (!$this->isGivenKind([T_NS_SEPARATOR, T_STRING], $tokens[$j])) {
5454
break;
5555
}
5656

PhpCollective/Sniffs/Commenting/AttributesSniff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function process(File $phpCsFile, $stackPointer): void
3838

3939
$tokens = $phpCsFile->getTokens();
4040

41-
if ($tokens[$nextIndex]['code'] === T_NS_SEPARATOR) {
41+
if ($tokens[$nextIndex]['code'] === T_NS_SEPARATOR || $tokens[$nextIndex]['code'] === T_NAME_FULLY_QUALIFIED) {
4242
return;
4343
}
4444

PhpCollective/Sniffs/Commenting/DisallowArrayTypeHintSyntaxSniff.php

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
use SlevomatCodingStandard\Helpers\AnnotationHelper;
2525
use SlevomatCodingStandard\Helpers\AnnotationTypeHelper;
2626
use SlevomatCodingStandard\Helpers\DocCommentHelper;
27-
use SlevomatCodingStandard\Helpers\FixerHelper;
2827
use SlevomatCodingStandard\Helpers\FunctionHelper;
2928
use SlevomatCodingStandard\Helpers\NamespaceHelper;
3029
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
@@ -143,23 +142,46 @@ public function process(File $phpcsFile, $pointer): void
143142
new IdentifierTypeNode($genericIdentifier),
144143
[$this->fixArrayNode($arrayTypeNode->type)],
145144
);
146-
$this->fixAnnotation($phpcsFile, $annotation, $genericTypeNode);
145+
$this->fixAnnotation($phpcsFile, $annotation, AnnotationTypeHelper::print($genericTypeNode));
147146

148147
continue;
149148
}
150149

151-
$phpcsFile->fixer->beginChangeset();
152-
FixerHelper::change(
150+
$this->applyFixWithoutTabConversion(
153151
$phpcsFile,
154152
$parsedDocComment->getOpenPointer(),
155153
$parsedDocComment->getClosePointer(),
156154
$fixedDocComment,
157155
);
158-
$phpcsFile->fixer->endChangeset();
159156
}
160157
}
161158
}
162159

160+
/**
161+
* Apply a fix without converting spaces to tabs
162+
*
163+
* @param \PHP_CodeSniffer\Files\File $phpcsFile
164+
* @param int $startPointer
165+
* @param int $endPointer
166+
* @param string $content
167+
*
168+
* @return void
169+
*/
170+
protected function applyFixWithoutTabConversion(File $phpcsFile, int $startPointer, int $endPointer, string $content): void
171+
{
172+
$phpcsFile->fixer->beginChangeset();
173+
174+
// Remove all tokens between start and end
175+
for ($i = $startPointer; $i <= $endPointer; $i++) {
176+
$phpcsFile->fixer->replaceToken($i, '');
177+
}
178+
179+
// Add the new content without tab conversion
180+
$phpcsFile->fixer->replaceToken($startPointer, $content);
181+
182+
$phpcsFile->fixer->endChangeset();
183+
}
184+
163185
/**
164186
* @param \PHP_CodeSniffer\Files\File $phpcsFile
165187
* @param \SlevomatCodingStandard\Helpers\Annotation $annotation
@@ -169,7 +191,7 @@ public function process(File $phpcsFile, $pointer): void
169191
*/
170192
protected function fixAnnotation(File $phpcsFile, Annotation $annotation, string $fixedAnnotation): void
171193
{
172-
/** @var \PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode|mixed $value */
194+
/** @var \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode $value */
173195
$value = $annotation->getNode()->value;
174196
$parameterName = $value->parameterName ?? '';
175197
$variableName = $value->variableName ?? '';
@@ -220,9 +242,9 @@ public function getArrayTypeNodes(Node $node): array
220242
/**
221243
* @param \PHPStan\PhpDocParser\Ast\Node $node
222244
*
223-
* @return \PHPStan\PhpDocParser\Ast\Node|list<\PHPStan\PhpDocParser\Ast\Node>|\PHPStan\PhpDocParser\Ast\NodeTraverser|int|null
245+
* @return int|null
224246
*/
225-
public function enterNode(Node $node): Node|array|\PHPStan\PhpDocParser\Ast\NodeTraverser|int|null
247+
public function enterNode(Node $node): int|null
226248
{
227249
if ($node instanceof ArrayTypeNode) {
228250
$this->nodes[] = $node;

PhpCollective/Sniffs/Commenting/DocBlockReturnVoidSniff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ protected function assertTypeHint(File $phpcsFile, int $stackPtr, int $docBlockR
320320
$typehint = '?' . $typehint;
321321
}
322322

323-
if ($documentedReturnType !== 'void' && $typeHintIndex !== 'void') {
323+
if ($documentedReturnType !== 'void' && $typehint !== 'void') {
324324
return;
325325
}
326326
if ($documentedReturnType === $typehint) {

PhpCollective/Sniffs/Commenting/DocBlockThrowsSniff.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ public function process(File $phpCsFile, $stackPointer): void
5151
return;
5252
}
5353

54-
if ($phpCsFile->getDeclarationName($stackPointer) === null) {
54+
try {
55+
$name = $phpCsFile->getDeclarationName($stackPointer);
56+
} catch (Exception $e) {
57+
return;
58+
}
59+
if (!$name) {
5560
return;
5661
}
5762

@@ -139,6 +144,12 @@ protected function extractExceptions(File $phpCsFile, int $stackPointer): array
139144
continue;
140145
}
141146

147+
// Handle the case where there's no namespace separator, string token, or fully qualified name token
148+
// (e.g., for parenthesis after 'new')
149+
if (!$this->isGivenKind([T_NS_SEPARATOR, T_STRING, T_NAME_FULLY_QUALIFIED], $tokens[$contentIndex])) {
150+
continue;
151+
}
152+
142153
$exceptions[] = $this->extractException($phpCsFile, $contentIndex);
143154

144155
continue;
@@ -223,9 +234,17 @@ protected function extractException(File $phpCsFile, int $contentIndex): array
223234
$fullClass = '';
224235

225236
$position = $contentIndex;
226-
while ($this->isGivenKind([T_NS_SEPARATOR, T_STRING], $tokens[$position])) {
227-
$fullClass .= $tokens[$position]['content'];
237+
238+
// Handle T_NAME_FULLY_QUALIFIED token (e.g., \BadMethodCallException)
239+
// or separate tokens (T_NS_SEPARATOR and T_STRING)
240+
if ($this->isGivenKind([T_NAME_FULLY_QUALIFIED], $tokens[$position])) {
241+
$fullClass = $tokens[$position]['content'];
228242
++$position;
243+
} else {
244+
while ($this->isGivenKind([T_NS_SEPARATOR, T_STRING], $tokens[$position])) {
245+
$fullClass .= $tokens[$position]['content'];
246+
++$position;
247+
}
229248
}
230249

231250
$class = $fullClass = ltrim($fullClass, '\\');

PhpCollective/Sniffs/Commenting/TypeHintSniff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public function process(File $phpcsFile, $stackPtr): void
126126
continue;
127127
}
128128

129-
/** @phpstan-var \PHPStan\PhpDocParser\Ast\Type\GenericTypeNode|\PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode $valueNode */
129+
/** @phpstan-var \PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode $valueNode */
130130
if ($valueNode->type instanceof UnionTypeNode) {
131131
$types = $valueNode->type->types;
132132
} elseif ($valueNode->type instanceof ArrayTypeNode) {

PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -291,39 +291,37 @@ public function processMultiLineArray(File $phpcsFile, int $stackPtr, int $array
291291
continue;
292292
}
293293

294-
if ($tokens[$nextToken]['code'] === T_DOUBLE_ARROW) {
295-
$currentEntry['arrow'] = $nextToken;
296-
$keyUsed = true;
294+
$currentEntry['arrow'] = $nextToken;
295+
$keyUsed = true;
297296

298-
// Find the start of index that uses this double arrow.
299-
$indexEnd = (int)$phpcsFile->findPrevious(T_WHITESPACE, ($nextToken - 1), $arrayStart, true);
300-
$indexStart = $phpcsFile->findStartOfStatement($indexEnd);
297+
// Find the start of index that uses this double arrow.
298+
$indexEnd = (int)$phpcsFile->findPrevious(T_WHITESPACE, ($nextToken - 1), $arrayStart, true);
299+
$indexStart = $phpcsFile->findStartOfStatement($indexEnd);
301300

302-
if ($indexStart === $indexEnd) {
303-
$currentEntry['index'] = $indexEnd;
304-
$currentEntry['index_content'] = $tokens[$indexEnd]['content'];
305-
} else {
306-
$currentEntry['index'] = $indexStart;
307-
$currentEntry['index_content'] = $phpcsFile->getTokensAsString($indexStart, ($indexEnd - $indexStart + 1));
308-
}
309-
310-
$indexLength = strlen($currentEntry['index_content']);
311-
if ($maxLength < $indexLength) {
312-
$maxLength = $indexLength;
313-
}
314-
315-
// Find the value of this index.
316-
$nextContent = $phpcsFile->findNext(
317-
Tokens::$emptyTokens,
318-
($nextToken + 1),
319-
$arrayEnd,
320-
true,
321-
);
301+
if ($indexStart === $indexEnd) {
302+
$currentEntry['index'] = $indexEnd;
303+
$currentEntry['index_content'] = $tokens[$indexEnd]['content'];
304+
} else {
305+
$currentEntry['index'] = $indexStart;
306+
$currentEntry['index_content'] = $phpcsFile->getTokensAsString($indexStart, ($indexEnd - $indexStart + 1));
307+
}
322308

323-
$currentEntry['value'] = $nextContent;
324-
$indices[] = $currentEntry;
325-
$lastToken = $nextToken;
309+
$indexLength = strlen($currentEntry['index_content']);
310+
if ($maxLength < $indexLength) {
311+
$maxLength = $indexLength;
326312
}
313+
314+
// Find the value of this index.
315+
$nextContent = $phpcsFile->findNext(
316+
Tokens::$emptyTokens,
317+
($nextToken + 1),
318+
$arrayEnd,
319+
true,
320+
);
321+
322+
$currentEntry['value'] = $nextContent;
323+
$indices[] = $currentEntry;
324+
$lastToken = $nextToken;
327325
}
328326

329327
$numValues = count($indices);

PhpCollective/Sniffs/Namespaces/UseStatementSniff.php

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ protected function checkUseForReturnTypeHint(File $phpcsFile, int $stackPtr): vo
574574
$extractedUseStatement = '';
575575
$lastSeparatorIndex = null;
576576
while (true) {
577-
if (!$this->isGivenKind([T_NS_SEPARATOR, T_STRING, T_RETURN_TYPE], $tokens[$j])) {
577+
if (!$this->isGivenKind([T_NS_SEPARATOR, T_STRING], $tokens[$j])) {
578578
break;
579579
}
580580

@@ -639,32 +639,45 @@ protected function checkPropertyForInstanceOf(File $phpcsFile, int $stackPtr): v
639639
return;
640640
}
641641

642-
$lastIndex = null;
643-
$j = $startIndex;
644-
$extractedUseStatement = '';
645-
$lastSeparatorIndex = null;
646-
while (true) {
647-
if (!$this->isGivenKind([T_NS_SEPARATOR, T_STRING, T_RETURN_TYPE], $tokens[$j])) {
648-
break;
642+
// Handle T_NAME_FULLY_QUALIFIED token (PHP CodeSniffer v4)
643+
$className = '';
644+
if ($tokens[$startIndex]['code'] === T_NAME_FULLY_QUALIFIED) {
645+
$extractedUseStatement = ltrim($tokens[$startIndex]['content'], '\\');
646+
if (strpos($extractedUseStatement, '\\') === false) {
647+
return; // Not a namespaced class
649648
}
649+
$lastSeparatorPos = strrpos($extractedUseStatement, '\\');
650+
$className = substr($extractedUseStatement, $lastSeparatorPos + 1);
651+
$lastIndex = $startIndex;
652+
$lastSeparatorIndex = null; // No separate separator token in v4
653+
} else {
654+
// Handle separate tokens (T_NS_SEPARATOR and T_STRING)
655+
$lastIndex = null;
656+
$j = $startIndex;
657+
$extractedUseStatement = '';
658+
$lastSeparatorIndex = null;
659+
while (true) {
660+
if (!$this->isGivenKind([T_NS_SEPARATOR, T_STRING], $tokens[$j])) {
661+
break;
662+
}
650663

651-
$lastIndex = $j;
652-
$extractedUseStatement .= $tokens[$j]['content'];
653-
if ($this->isGivenKind([T_NS_SEPARATOR], $tokens[$j])) {
654-
$lastSeparatorIndex = $j;
664+
$lastIndex = $j;
665+
$extractedUseStatement .= $tokens[$j]['content'];
666+
if ($this->isGivenKind([T_NS_SEPARATOR], $tokens[$j])) {
667+
$lastSeparatorIndex = $j;
668+
}
669+
++$j;
655670
}
656-
++$j;
657-
}
658671

659-
if ($lastIndex === null || $lastSeparatorIndex === null) {
660-
return;
661-
}
662-
663-
$extractedUseStatement = ltrim($extractedUseStatement, '\\');
672+
if ($lastIndex === null || $lastSeparatorIndex === null) {
673+
return;
674+
}
664675

665-
$className = '';
666-
for ($k = $lastSeparatorIndex + 1; $k <= $lastIndex; ++$k) {
667-
$className .= $tokens[$k]['content'];
676+
$extractedUseStatement = ltrim($extractedUseStatement, '\\');
677+
$className = '';
678+
for ($k = $lastSeparatorIndex + 1; $k <= $lastIndex; ++$k) {
679+
$className .= $tokens[$k]['content'];
680+
}
668681
}
669682

670683
$error = 'Use statement ' . $extractedUseStatement . ' for class ' . $className . ' should be in use block.';
@@ -677,13 +690,23 @@ protected function checkPropertyForInstanceOf(File $phpcsFile, int $stackPtr): v
677690

678691
$addedUseStatement = $this->addUseStatement($phpcsFile, $className, $extractedUseStatement);
679692

680-
for ($k = $lastSeparatorIndex; $k > $startIndex; --$k) {
681-
$phpcsFile->fixer->replaceToken($k, '');
682-
}
683-
$phpcsFile->fixer->replaceToken($startIndex, '');
693+
if ($lastSeparatorIndex !== null) {
694+
// Legacy: remove individual tokens
695+
for ($k = $lastSeparatorIndex; $k > $startIndex; --$k) {
696+
$phpcsFile->fixer->replaceToken($k, '');
697+
}
698+
$phpcsFile->fixer->replaceToken($startIndex, '');
684699

685-
if ($addedUseStatement['alias'] !== null) {
686-
$phpcsFile->fixer->replaceToken($lastIndex, $addedUseStatement['alias']);
700+
if ($addedUseStatement['alias'] !== null) {
701+
$phpcsFile->fixer->replaceToken($lastIndex, $addedUseStatement['alias']);
702+
}
703+
} else {
704+
// PHP CodeSniffer v4: replace single T_NAME_FULLY_QUALIFIED token
705+
if ($addedUseStatement['alias'] !== null) {
706+
$phpcsFile->fixer->replaceToken($startIndex, $addedUseStatement['alias']);
707+
} else {
708+
$phpcsFile->fixer->replaceToken($startIndex, $className);
709+
}
687710
}
688711

689712
$phpcsFile->fixer->endChangeset();

0 commit comments

Comments
 (0)