From 1ac6250e665830b6da2729f527d8f358d2cf7986 Mon Sep 17 00:00:00 2001 From: mscherer Date: Thu, 31 Jul 2025 20:01:24 +0200 Subject: [PATCH 01/13] Add more useful sniffs. --- PhpCollective/ruleset.xml | 10 +++++++++- composer.json | 1 + docs/sniffs.md | 18 +++++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/PhpCollective/ruleset.xml b/PhpCollective/ruleset.xml index 1530cbe..1fb87a3 100644 --- a/PhpCollective/ruleset.xml +++ b/PhpCollective/ruleset.xml @@ -18,7 +18,7 @@ *\.txt *\.json - + @@ -250,6 +250,14 @@ + + + + + + + + diff --git a/composer.json b/composer.json index a9f15ff..add72e5 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "squizlabs/php_codesniffer": "^3.11.3" }, "require-dev": { + "phpcsstandards/phpcsextra": "^1.2.0", "phpstan/phpstan": "^1.0.0", "phpunit/phpunit": "^10.3 || ^11.2 || ^12.0" }, diff --git a/docs/sniffs.md b/docs/sniffs.md index 4c782a0..060dc9f 100644 --- a/docs/sniffs.md +++ b/docs/sniffs.md @@ -1,7 +1,7 @@ # PhpCollective Code Sniffer -The PhpCollectiveStrict standard contains 225 sniffs +The PhpCollectiveStrict standard contains 232 sniffs Generic (26 sniffs) ------------------- @@ -32,6 +32,14 @@ Generic (26 sniffs) - Generic.WhiteSpace.LanguageConstructSpacing - Generic.WhiteSpace.ScopeIndent +Modernize (1 sniff) +------------------- +- Modernize.FunctionCalls.Dirname + +NormalizedArrays (1 sniff) +-------------------------- +- NormalizedArrays.Arrays.ArrayBraceSpacing + PEAR (4 sniffs) --------------- - PEAR.Classes.ClassDeclaration @@ -254,6 +262,14 @@ Squiz (27 sniffs) - Squiz.WhiteSpace.SemicolonSpacing - Squiz.WhiteSpace.SuperfluousWhitespace +Universal (5 sniffs) +-------------------- +- Universal.Constants.LowercaseClassResolutionKeyword +- Universal.Constants.UppercaseMagicConstants +- Universal.Operators.ConcatPosition +- Universal.UseStatements.NoUselessAliases +- Universal.WhiteSpace.PrecisionAlignment + Zend (1 sniff) -------------- - Zend.Files.ClosingTag From 702121d4f92f6830aed2d3d1a9d14c9251750c9b Mon Sep 17 00:00:00 2001 From: mscherer Date: Thu, 31 Jul 2025 20:11:00 +0200 Subject: [PATCH 02/13] Add more useful sniffs. --- PhpCollective/Sniffs/Classes/Psr4Sniff.php | 4 ++-- PhpCollective/Sniffs/Internal/DisallowFunctionsSniff.php | 3 +-- PhpCollective/Sniffs/PHP/DeclareStrictTypesSniff.php | 8 +++++++- PhpCollective/Tools/Tokenizer.php | 2 +- PhpCollective/ruleset.xml | 1 + docs/sniffs.md | 5 +++-- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/PhpCollective/Sniffs/Classes/Psr4Sniff.php b/PhpCollective/Sniffs/Classes/Psr4Sniff.php index 850dfd9..1624d15 100644 --- a/PhpCollective/Sniffs/Classes/Psr4Sniff.php +++ b/PhpCollective/Sniffs/Classes/Psr4Sniff.php @@ -142,8 +142,8 @@ protected function addError( $phpcsFile->addError( sprintf( - 'Class name is not compliant with PSR-4 configuration. ' . - 'It should be `%s` instead of `%s`.', + 'Class name is not compliant with PSR-4 configuration. ' + . 'It should be `%s` instead of `%s`.', $result->getExpectedClassName(), $result->getActualClassName(), ), diff --git a/PhpCollective/Sniffs/Internal/DisallowFunctionsSniff.php b/PhpCollective/Sniffs/Internal/DisallowFunctionsSniff.php index a0712ca..1fcdae3 100644 --- a/PhpCollective/Sniffs/Internal/DisallowFunctionsSniff.php +++ b/PhpCollective/Sniffs/Internal/DisallowFunctionsSniff.php @@ -42,8 +42,7 @@ class DisallowFunctionsSniff extends AbstractSniff /** * @var array> */ - protected static array $methods = [ - ]; + protected static array $methods = []; /** * @var bool|null diff --git a/PhpCollective/Sniffs/PHP/DeclareStrictTypesSniff.php b/PhpCollective/Sniffs/PHP/DeclareStrictTypesSniff.php index fcba959..aa21bc0 100644 --- a/PhpCollective/Sniffs/PHP/DeclareStrictTypesSniff.php +++ b/PhpCollective/Sniffs/PHP/DeclareStrictTypesSniff.php @@ -18,7 +18,13 @@ class DeclareStrictTypesSniff implements Sniff * @var array */ protected const TOKEN_CODES_TO_CHECK = [ - T_OPEN_PARENTHESIS, T_STRING, T_EQUAL, T_LNUMBER, T_CLOSE_PARENTHESIS, T_SEMICOLON, T_COMMENT, + T_OPEN_PARENTHESIS, + T_STRING, + T_EQUAL, + T_LNUMBER, + T_CLOSE_PARENTHESIS, + T_SEMICOLON, + T_COMMENT, ]; /** diff --git a/PhpCollective/Tools/Tokenizer.php b/PhpCollective/Tools/Tokenizer.php index 25008d1..3d27480 100644 --- a/PhpCollective/Tools/Tokenizer.php +++ b/PhpCollective/Tools/Tokenizer.php @@ -52,7 +52,7 @@ public function __construct(array $argv) throw new Exception('Please provide a valid file.'); } - $this->root = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR; + $this->root = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR; $this->path = $file; $this->verbose = !empty($argv[2]) && in_array($argv[2], ['--verbose', '-v']); } diff --git a/PhpCollective/ruleset.xml b/PhpCollective/ruleset.xml index 1fb87a3..a45bd66 100644 --- a/PhpCollective/ruleset.xml +++ b/PhpCollective/ruleset.xml @@ -250,6 +250,7 @@ + diff --git a/docs/sniffs.md b/docs/sniffs.md index 060dc9f..8073708 100644 --- a/docs/sniffs.md +++ b/docs/sniffs.md @@ -1,10 +1,11 @@ # PhpCollective Code Sniffer -The PhpCollectiveStrict standard contains 232 sniffs +The PhpCollectiveStrict standard contains 233 sniffs -Generic (26 sniffs) +Generic (27 sniffs) ------------------- +- Generic.Arrays.ArrayIndent - Generic.Arrays.DisallowLongArraySyntax - Generic.CodeAnalysis.ForLoopShouldBeWhileLoop - Generic.CodeAnalysis.ForLoopWithTestFunctionCall From de37df96c81c081808cb44d547ed77a3b7ea728c Mon Sep 17 00:00:00 2001 From: mscherer Date: Thu, 31 Jul 2025 20:12:52 +0200 Subject: [PATCH 03/13] Add more useful sniffs. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index add72e5..44713cf 100644 --- a/composer.json +++ b/composer.json @@ -23,11 +23,11 @@ ], "require": { "php": ">=8.1", + "phpcsstandards/phpcsextra": "^1.2.0", "slevomat/coding-standard": "^8.16.0", "squizlabs/php_codesniffer": "^3.11.3" }, "require-dev": { - "phpcsstandards/phpcsextra": "^1.2.0", "phpstan/phpstan": "^1.0.0", "phpunit/phpunit": "^10.3 || ^11.2 || ^12.0" }, From ebbad2d24f512a193a580d3c7a4b843c3a046957 Mon Sep 17 00:00:00 2001 From: mscherer Date: Thu, 31 Jul 2025 20:57:12 +0200 Subject: [PATCH 04/13] Make sure arrays are structured with one key per level. --- ...FullyQualifiedClassNameInDocBlockSniff.php | 21 +- .../Formatting/ArrayDeclarationSniff.php | 278 ++++++++++++++++-- .../Formatting/ArrayDeclarationSniffTest.php | 30 ++ tests/_data/ArrayDeclaration/after.php | 21 ++ tests/_data/ArrayDeclaration/before.php | 17 ++ 5 files changed, 338 insertions(+), 29 deletions(-) create mode 100644 tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php create mode 100644 tests/_data/ArrayDeclaration/after.php create mode 100644 tests/_data/ArrayDeclaration/before.php diff --git a/PhpCollective/Sniffs/Commenting/FullyQualifiedClassNameInDocBlockSniff.php b/PhpCollective/Sniffs/Commenting/FullyQualifiedClassNameInDocBlockSniff.php index b67d381..dbad8e5 100644 --- a/PhpCollective/Sniffs/Commenting/FullyQualifiedClassNameInDocBlockSniff.php +++ b/PhpCollective/Sniffs/Commenting/FullyQualifiedClassNameInDocBlockSniff.php @@ -27,8 +27,25 @@ class FullyQualifiedClassNameInDocBlockSniff implements Sniff * @var array */ public static array $whitelistedTypes = [ - 'string', 'int', 'integer', 'float', 'bool', 'boolean', 'resource', 'null', 'void', 'callable', - 'array', 'iterable', 'mixed', 'object', 'false', 'true', 'self', 'static', '$this', + 'string', + 'int', + 'integer', + 'float', + 'bool', + 'boolean', + 'resource', + 'null', + 'void', + 'callable', + 'array', + 'iterable', + 'mixed', + 'object', + 'false', + 'true', + 'self', + 'static', + '$this', ]; /** diff --git a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php index 05eafce..415ae1d 100644 --- a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php +++ b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php @@ -54,6 +54,7 @@ public function process(File $phpcsFile, $stackPtr): void $this->processSingleLineArray($phpcsFile, $arrayStart, $arrayEnd); } else { $this->processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd); + $this->processMultiLineIndentation($phpcsFile, $arrayStart, $arrayEnd); } } @@ -293,33 +294,6 @@ public function processMultiLineArray(File $phpcsFile, int $stackPtr, int $array } } - /* - Below the actual indentation of the array is checked. - Errors will be thrown when a key is not aligned, when - a double arrow is not aligned, and when a value is not - aligned correctly. - If an error is found in one of the above areas, then errors - are not reported for the rest of the line to avoid reporting - spaces and columns incorrectly. Often fixing the first - problem will fix the other 2 anyway. - - For example: - - $a = array( - 'index' => '2', - ); - - or - - $a = [ - 'index' => '2', - ]; - - In this array, the double arrow is indented too far, but this - will also cause an error in the value's alignment. If the arrow were - to be moved back one space however, then both errors would be fixed. - */ - $numValues = count($indices); foreach ($indices as $index) { @@ -356,4 +330,254 @@ public function processMultiLineArray(File $phpcsFile, int $stackPtr, int $array } } } + + public function processMultiLineIndentation(File $phpcsFile, int $arrayStart, int $arrayEnd): void + { + $tokens = $phpcsFile->getTokens(); + $pairs = []; + + $i = $arrayStart + 1; + while ($i < $arrayEnd) { + $token = $tokens[$i]; + + if (in_array($token['code'], Tokens::$emptyTokens, true)) { + $i++; + + continue; + } + + // Skip over nested structures (function calls, arrays, etc.) + if ($token['code'] === T_OPEN_PARENTHESIS && isset($token['parenthesis_closer'])) { + $i = $token['parenthesis_closer'] + 1; + + continue; + } + if ($token['code'] === T_OPEN_SHORT_ARRAY && isset($token['bracket_closer'])) { + $i = $token['bracket_closer'] + 1; + + continue; + } + if ($token['code'] === T_ARRAY && isset($token['parenthesis_closer'])) { + $i = $token['parenthesis_closer'] + 1; + + continue; + } + + // Handle key => value + if ($token['code'] === T_DOUBLE_ARROW) { + $keyEnd = $phpcsFile->findPrevious(T_WHITESPACE, $i - 1, $arrayStart, true); + if ($keyEnd === false) { + break; + } + $keyStart = $phpcsFile->findStartOfStatement($keyEnd); + + $valueStart = $phpcsFile->findNext(Tokens::$emptyTokens, $i + 1, $arrayEnd, true); + if ($valueStart === false) { + break; + } + + // Find the end of the value expression (handles function calls, etc.) + $valueEnd = $valueStart; + $depth = 0; + + for ($j = $valueStart; $j < $arrayEnd; $j++) { + $currentToken = $tokens[$j]; + + // Handle string literals + if (in_array($currentToken['code'], [T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_QUOTED_STRING], true)) { + $valueEnd = $j; + + continue; + } + + // Track parentheses depth + if ($currentToken['code'] === T_OPEN_PARENTHESIS) { + $depth++; + } elseif ($currentToken['code'] === T_CLOSE_PARENTHESIS) { + $depth--; + } + + // Stop at comma when we're at depth 0 (not inside function call) + if ($currentToken['code'] === T_COMMA && $depth === 0) { + break; + } + + // Skip whitespace and comments when determining end + if (!in_array($currentToken['code'], Tokens::$emptyTokens, true)) { + $valueEnd = $j; + } + } + + $pairs[] = [ + 'key' => $keyStart, + 'arrow' => $i, + 'value' => $valueStart, + 'value_end' => $valueEnd, + 'line' => $tokens[$keyStart]['line'], + ]; + + $i = $phpcsFile->findNext([T_COMMA], $valueEnd + 1, $arrayEnd); + if ($i === false) { + break; + } + + $i++; + + continue; + } + + // Handle single value (non-associative) + if ($token['code'] !== T_COMMA) { + // Find the end of this value expression (handles function calls, etc.) + $valueEnd = $i; + $depth = 0; + + for ($j = $i; $j < $arrayEnd; $j++) { + $currentToken = $tokens[$j]; + + // Handle string literals + if (in_array($currentToken['code'], [T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_QUOTED_STRING], true)) { + $valueEnd = $j; + + continue; + } + + // Track parentheses depth + if ($currentToken['code'] === T_OPEN_PARENTHESIS) { + $depth++; + } elseif ($currentToken['code'] === T_CLOSE_PARENTHESIS) { + $depth--; + } + + // Stop at comma when we're at depth 0 (not inside function call) + if ($currentToken['code'] === T_COMMA && $depth === 0) { + break; + } + + // Skip whitespace and comments when determining end + if (!in_array($currentToken['code'], Tokens::$emptyTokens, true)) { + $valueEnd = $j; + } + } + + $pairs[] = [ + 'key' => null, + 'arrow' => null, + 'value' => $i, + 'value_end' => $valueEnd, + 'line' => $tokens[$i]['line'], + ]; + + $i = $phpcsFile->findNext([T_COMMA], $valueEnd + 1, $arrayEnd); + if ($i === false) { + break; + } + $i++; + } else { + $i++; + } + } + + // Group by line, but only if fully single-line expressions + $lineCounts = []; + foreach ($pairs as $pair) { + $startLine = $tokens[$pair['key'] ?? $pair['value']]['line']; + $endLine = $tokens[$pair['value_end'] ?: $pair['value']]['line']; + + if ($startLine === $endLine) { + $lineCounts[$startLine][] = $pair; + } + } + + foreach ($lineCounts as $line => $items) { + if (count($items) < 2) { + continue; + } + + foreach ($items as $i => $pair) { + $ptr = $pair['key'] ?? $pair['value']; + $error = 'Each array item must be on its own line in a multi-line array'; + $fix = $phpcsFile->addFixableError($error, $ptr, 'MultipleItemsPerLine'); + + if ($fix) { + // Calculate proper indentation by looking at existing properly indented items in this array + $baseIndent = ''; + + // Look for the first properly indented item in this array to match its indentation + for ($searchIdx = $arrayStart + 1; $searchIdx < $arrayEnd; $searchIdx++) { + if ( + $tokens[$searchIdx]['line'] > $tokens[$arrayStart]['line'] && + !in_array($tokens[$searchIdx]['code'], Tokens::$emptyTokens, true) + ) { + // Extract actual indentation from the line + $lineStart = $phpcsFile->findFirstOnLine([], $searchIdx); + if ($lineStart !== false && $lineStart < $searchIdx) { + $baseIndent = $phpcsFile->getTokensAsString($lineStart, $searchIdx - $lineStart); + } else { + // Fallback to column-based calculation + $indentLevel = $tokens[$searchIdx]['column'] - 1; + $baseIndent = str_repeat(' ', $indentLevel); + } + + break; + } + } + + // Fallback: detect indentation style and calculate based on array position + if ($baseIndent === '') { + // Detect if file uses tabs or spaces by looking at existing indentation + $usesTabs = false; + $indentSize = 4; // Default to 4 spaces + + // Scan the file to detect indentation style + $count = count($tokens); + for ($detectIdx = 0; $detectIdx < $count; $detectIdx++) { + if ( + $tokens[$detectIdx]['code'] === T_WHITESPACE && + $tokens[$detectIdx]['line'] !== $tokens[$detectIdx - 1]['line'] + ) { + $whitespace = $tokens[$detectIdx]['content']; + if (str_contains($whitespace, "\t")) { + $usesTabs = true; + + break; + } elseif (strlen($whitespace) > 0) { + // Count spaces to determine indent size + $spaceCount = strlen(str_replace(["\n", "\r"], '', $whitespace)); + if ($spaceCount > 0 && $spaceCount % 4 === 0) { + $indentSize = 4; + } elseif ($spaceCount > 0 && $spaceCount % 2 === 0) { + $indentSize = 2; + } + } + } + } + + // Calculate indentation based on array nesting + $arrayColumn = $tokens[$arrayStart]['column']; + $indentLevel = (int)(($arrayColumn + 3) / ($usesTabs ? 1 : $indentSize)); + + if ($usesTabs) { + $baseIndent = str_repeat("\t", $indentLevel); + } else { + $baseIndent = str_repeat(' ', $arrayColumn + 3); + } + } + + $phpcsFile->fixer->beginChangeset(); + foreach ($items as $j => $p) { + if ($j === 0) { + continue; + } + + $targetPtr = $p['key'] ?? $p['value']; + $phpcsFile->fixer->addContentBefore($targetPtr, "\n" . $baseIndent); + } + $phpcsFile->fixer->endChangeset(); + } + + break; // Only one error per line + } + } + } } diff --git a/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php b/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php new file mode 100644 index 0000000..4913a36 --- /dev/null +++ b/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php @@ -0,0 +1,30 @@ +assertSnifferFindsErrors(new ArrayDeclarationSniff(), 2); + } + + /** + * @return void + */ + public function testDocBlockConstFixer(): void + { + $this->assertSnifferCanFixErrors(new ArrayDeclarationSniff()); + } +} diff --git a/tests/_data/ArrayDeclaration/after.php b/tests/_data/ArrayDeclaration/after.php new file mode 100644 index 0000000..cb18deb --- /dev/null +++ b/tests/_data/ArrayDeclaration/after.php @@ -0,0 +1,21 @@ + 'b' . 'c', + 'x' => [ + 'y' => 'z', + 'z' => 'a', + ], + 'c' => __FILE__, + 'd' => Xyz::class, + 'r', + 'content' => $this->getContent($tokens, $i, $tagEnd), + ]; + } +} diff --git a/tests/_data/ArrayDeclaration/before.php b/tests/_data/ArrayDeclaration/before.php new file mode 100644 index 0000000..75fecf3 --- /dev/null +++ b/tests/_data/ArrayDeclaration/before.php @@ -0,0 +1,17 @@ + 'b' . 'c', + 'x' => [ + 'y' => 'z', 'z' => 'a', + ], + 'c' => __FILE__, 'd' => Xyz::class, 'r', 'content' => $this->getContent($tokens, $i, $tagEnd), + ]; + } +} From a51096ed9e40c55aace9b670fc78e9bfbbd5a819 Mon Sep 17 00:00:00 2001 From: mscherer Date: Thu, 31 Jul 2025 21:33:05 +0200 Subject: [PATCH 05/13] Make sure arrays are structured with one key per level. --- .../Formatting/ArrayDeclarationSniff.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php index 415ae1d..d20f3bf 100644 --- a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php +++ b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php @@ -509,10 +509,14 @@ public function processMultiLineIndentation(File $phpcsFile, int $arrayStart, in $tokens[$searchIdx]['line'] > $tokens[$arrayStart]['line'] && !in_array($tokens[$searchIdx]['code'], Tokens::$emptyTokens, true) ) { - // Extract actual indentation from the line + // Extract actual indentation from the line (only leading whitespace) $lineStart = $phpcsFile->findFirstOnLine([], $searchIdx); if ($lineStart !== false && $lineStart < $searchIdx) { - $baseIndent = $phpcsFile->getTokensAsString($lineStart, $searchIdx - $lineStart); + $indentContent = $phpcsFile->getTokensAsString($lineStart, $searchIdx - $lineStart); + // Only keep leading whitespace (tabs and spaces), remove any other characters + if (preg_match('/^[\t ]*/', $indentContent, $matches)) { + $baseIndent = $matches[0]; + } } else { // Fallback to column-based calculation $indentLevel = $tokens[$searchIdx]['column'] - 1; @@ -571,6 +575,16 @@ public function processMultiLineIndentation(File $phpcsFile, int $arrayStart, in } $targetPtr = $p['key'] ?? $p['value']; + + // Find any whitespace before the target token and remove it + $prevToken = $targetPtr - 1; + while ($prevToken >= $arrayStart && in_array($tokens[$prevToken]['code'], [T_WHITESPACE, T_COMMA], true)) { + if ($tokens[$prevToken]['code'] === T_WHITESPACE) { + $phpcsFile->fixer->replaceToken($prevToken, ''); + } + $prevToken--; + } + $phpcsFile->fixer->addContentBefore($targetPtr, "\n" . $baseIndent); } $phpcsFile->fixer->endChangeset(); From 75f2c2ffcdc6a9b381b9abc31868a4d234bff35e Mon Sep 17 00:00:00 2001 From: mscherer Date: Sat, 2 Aug 2025 13:27:23 +0200 Subject: [PATCH 06/13] Make sure arrays are structured with one key per level. --- .../Formatting/ArrayDeclarationSniff.php | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php index d20f3bf..6cbec1a 100644 --- a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php +++ b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php @@ -24,6 +24,17 @@ */ class ArrayDeclarationSniff implements Sniff { + /** + * Controls when multi-line indentation rules are applied. + * + * Options: + * - 'assoc' (default): Only enforce one item per line for associative arrays + * - 'all': Enforce one item per line for all arrays (both associative and indexed) + * + * @var string + */ + public string $multiLineIndentationMode = 'assoc'; + /** * @inheritDoc */ @@ -331,7 +342,7 @@ public function processMultiLineArray(File $phpcsFile, int $stackPtr, int $array } } - public function processMultiLineIndentation(File $phpcsFile, int $arrayStart, int $arrayEnd): void + protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, int $arrayEnd): void { $tokens = $phpcsFile->getTokens(); $pairs = []; @@ -414,6 +425,7 @@ public function processMultiLineIndentation(File $phpcsFile, int $arrayStart, in 'value' => $valueStart, 'value_end' => $valueEnd, 'line' => $tokens[$keyStart]['line'], + 'is_associative' => true, ]; $i = $phpcsFile->findNext([T_COMMA], $valueEnd + 1, $arrayEnd); @@ -466,6 +478,7 @@ public function processMultiLineIndentation(File $phpcsFile, int $arrayStart, in 'value' => $i, 'value_end' => $valueEnd, 'line' => $tokens[$i]['line'], + 'is_associative' => false, ]; $i = $phpcsFile->findNext([T_COMMA], $valueEnd + 1, $arrayEnd); @@ -494,7 +507,31 @@ public function processMultiLineIndentation(File $phpcsFile, int $arrayStart, in continue; } + // Check if we should process these items based on configuration + $shouldProcess = false; + if ($this->multiLineIndentationMode === 'all') { + $shouldProcess = true; + } else { + // In 'assoc' mode, only process if at least one item on this line is associative + foreach ($items as $item) { + if ($item['is_associative']) { + $shouldProcess = true; + + break; + } + } + } + + if (!$shouldProcess) { + continue; + } + foreach ($items as $i => $pair) { + // In 'assoc' mode, only flag associative items + if ($this->multiLineIndentationMode === 'assoc' && !$pair['is_associative']) { + continue; + } + $ptr = $pair['key'] ?? $pair['value']; $error = 'Each array item must be on its own line in a multi-line array'; $fix = $phpcsFile->addFixableError($error, $ptr, 'MultipleItemsPerLine'); @@ -574,6 +611,11 @@ public function processMultiLineIndentation(File $phpcsFile, int $arrayStart, in continue; } + // In 'assoc' mode, only fix associative items + if ($this->multiLineIndentationMode === 'assoc' && !$p['is_associative']) { + continue; + } + $targetPtr = $p['key'] ?? $p['value']; // Find any whitespace before the target token and remove it From 620aede5fcf1a3dccef6ec14615f9bd303452347 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sat, 9 Aug 2025 02:50:35 +0200 Subject: [PATCH 07/13] Fix false positive in DocBlockVarSniff for class aliases. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves issue where properties using class aliases from use statements incorrectly triggered doc block type errors. Added typesMatch() method to properly compare doc block types with typed properties by resolving aliases through use statements. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../Sniffs/Commenting/DocBlockVarSniff.php | 35 ++++++++++++++++++- tests/_data/DocBlockVar/after.php | 19 ++++++++++ tests/_data/DocBlockVar/before.php | 19 ++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php b/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php index b3e4c66..0ca4140 100644 --- a/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php +++ b/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php @@ -11,6 +11,7 @@ use PHP_CodeSniffer\Util\Tokens; use PhpCollective\Sniffs\AbstractSniffs\AbstractSniff; use PhpCollective\Traits\CommentingTrait; +use PhpCollective\Traits\UseStatementsTrait; /** * Ensures Doc Blocks for variables exist and are correct. @@ -21,6 +22,7 @@ class DocBlockVarSniff extends AbstractSniff { use CommentingTrait; + use UseStatementsTrait; /** * @inheritDoc @@ -377,7 +379,7 @@ protected function handleDefaultValue( protected function handleTypes(File $phpCsFile, int $stackPointer, array $types, mixed $content, string $appendix, int $classNameIndex): void { foreach ($types as $type) { - if (str_contains($content, $type)) { + if ($this->typesMatch($phpCsFile, $content, $type)) { continue; } @@ -387,4 +389,35 @@ protected function handleTypes(File $phpCsFile, int $stackPointer, array $types, } } } + + /** + * Check if two types match, considering use statement aliases. + * + * @param \PHP_CodeSniffer\Files\File $phpCsFile + * @param string $docBlockType + * @param string $propertyType + * + * @return bool + */ + protected function typesMatch(File $phpCsFile, string $docBlockType, string $propertyType): bool + { + // Direct match + if (str_contains($docBlockType, $propertyType)) { + return true; + } + + // Get use statements + $useStatements = $this->getUseStatements($phpCsFile); + + // Check if the property type is an alias + if (isset($useStatements[$propertyType])) { + $fullClassName = $useStatements[$propertyType]['fullName']; + // Check if doc block contains the full class name (with or without leading backslash) + if (str_contains($docBlockType, '\\' . $fullClassName) || str_contains($docBlockType, $fullClassName)) { + return true; + } + } + + return false; + } } diff --git a/tests/_data/DocBlockVar/after.php b/tests/_data/DocBlockVar/after.php index 481acd3..0cb687c 100644 --- a/tests/_data/DocBlockVar/after.php +++ b/tests/_data/DocBlockVar/after.php @@ -2,6 +2,10 @@ namespace PhpCollective; +use Tools\Mailer\Message as MailerMessage; +use Some\Other\Class; +use Yet\Another\Thing as AnotherAlias; + class FixMe { protected $x = []; @@ -29,4 +33,19 @@ class FixMe * @var list */ protected $left = []; + + /** + * @var \Tools\Mailer\Message + */ + protected MailerMessage $message; + + /** + * @var \Some\Other\Class + */ + protected Class $class; + + /** + * @var \Yet\Another\Thing + */ + protected AnotherAlias $another; } diff --git a/tests/_data/DocBlockVar/before.php b/tests/_data/DocBlockVar/before.php index 481acd3..0cb687c 100644 --- a/tests/_data/DocBlockVar/before.php +++ b/tests/_data/DocBlockVar/before.php @@ -2,6 +2,10 @@ namespace PhpCollective; +use Tools\Mailer\Message as MailerMessage; +use Some\Other\Class; +use Yet\Another\Thing as AnotherAlias; + class FixMe { protected $x = []; @@ -29,4 +33,19 @@ class FixMe * @var list */ protected $left = []; + + /** + * @var \Tools\Mailer\Message + */ + protected MailerMessage $message; + + /** + * @var \Some\Other\Class + */ + protected Class $class; + + /** + * @var \Yet\Another\Thing + */ + protected AnotherAlias $another; } From 9fb0e4dfab04ef81e33e31a6b9327298fd21839e Mon Sep 17 00:00:00 2001 From: mscherer Date: Sat, 9 Aug 2025 02:54:15 +0200 Subject: [PATCH 08/13] Fix false positive. --- tests/_data/DocBlockVar/after.php | 4 ++-- tests/_data/DocBlockVar/before.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/_data/DocBlockVar/after.php b/tests/_data/DocBlockVar/after.php index 0cb687c..a76fb9e 100644 --- a/tests/_data/DocBlockVar/after.php +++ b/tests/_data/DocBlockVar/after.php @@ -40,9 +40,9 @@ class FixMe protected MailerMessage $message; /** - * @var \Some\Other\Class + * @var \Some\Other\MyClass|null */ - protected Class $class; + protected ?MyClass $class; /** * @var \Yet\Another\Thing diff --git a/tests/_data/DocBlockVar/before.php b/tests/_data/DocBlockVar/before.php index 0cb687c..a76fb9e 100644 --- a/tests/_data/DocBlockVar/before.php +++ b/tests/_data/DocBlockVar/before.php @@ -40,9 +40,9 @@ class FixMe protected MailerMessage $message; /** - * @var \Some\Other\Class + * @var \Some\Other\MyClass|null */ - protected Class $class; + protected ?MyClass $class; /** * @var \Yet\Another\Thing From 32bc36ac94c0db23bac306ad7dfb3440135b2b17 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sat, 9 Aug 2025 02:58:58 +0200 Subject: [PATCH 09/13] Fix false positive in DocBlockVarSniff for array property types. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves issue where the sniff incorrectly reported errors for array properties using alternative syntax like string[], int[], or array in doc blocks when the property was typed as array. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../Sniffs/Commenting/DocBlockVarSniff.php | 5 ++++ tests/_data/DocBlockVar/after.php | 24 ++++++++++++++++++- tests/_data/DocBlockVar/before.php | 24 ++++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php b/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php index 0ca4140..48c229d 100644 --- a/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php +++ b/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php @@ -406,6 +406,11 @@ protected function typesMatch(File $phpCsFile, string $docBlockType, string $pro return true; } + // Check if property type is 'array' and doc block contains array syntax + if ($propertyType === 'array' && $this->containsTypeArray([$docBlockType])) { + return true; + } + // Get use statements $useStatements = $this->getUseStatements($phpCsFile); diff --git a/tests/_data/DocBlockVar/after.php b/tests/_data/DocBlockVar/after.php index a76fb9e..341ff37 100644 --- a/tests/_data/DocBlockVar/after.php +++ b/tests/_data/DocBlockVar/after.php @@ -32,7 +32,7 @@ class FixMe * * @var list */ - protected $left = []; + protected array $left = []; /** * @var \Tools\Mailer\Message @@ -48,4 +48,26 @@ class FixMe * @var \Yet\Another\Thing */ protected AnotherAlias $another; + + /** + * @var string[] + */ + protected array $fixtures = [ + 'plugin.QueueScheduler.SchedulerRows', + ]; + + /** + * @var int[] + */ + protected array $numbers = [1, 2, 3]; + + /** + * @var array + */ + protected array $config = []; + + /** + * @var MyClass[] + */ + protected array $objects; } diff --git a/tests/_data/DocBlockVar/before.php b/tests/_data/DocBlockVar/before.php index a76fb9e..341ff37 100644 --- a/tests/_data/DocBlockVar/before.php +++ b/tests/_data/DocBlockVar/before.php @@ -32,7 +32,7 @@ class FixMe * * @var list */ - protected $left = []; + protected array $left = []; /** * @var \Tools\Mailer\Message @@ -48,4 +48,26 @@ class FixMe * @var \Yet\Another\Thing */ protected AnotherAlias $another; + + /** + * @var string[] + */ + protected array $fixtures = [ + 'plugin.QueueScheduler.SchedulerRows', + ]; + + /** + * @var int[] + */ + protected array $numbers = [1, 2, 3]; + + /** + * @var array + */ + protected array $config = []; + + /** + * @var MyClass[] + */ + protected array $objects; } From d6fec81ff3a03e7cc3b3cf12d527f6f0c92b0995 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sat, 9 Aug 2025 03:16:25 +0200 Subject: [PATCH 10/13] Fix false positive. --- .../Sniffs/Formatting/ArrayDeclarationSniff.php | 12 ++++++++++++ tests/_data/ArrayDeclaration/after.php | 3 +-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php index 6cbec1a..99768db 100644 --- a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php +++ b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php @@ -363,6 +363,8 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, continue; } + // Note: Nested arrays are processed separately by their own process() call, + // so we should skip them here to avoid double-processing if ($token['code'] === T_OPEN_SHORT_ARRAY && isset($token['bracket_closer'])) { $i = $token['bracket_closer'] + 1; @@ -440,6 +442,15 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, // Handle single value (non-associative) if ($token['code'] !== T_COMMA) { + // Check if this might be a key by looking ahead for => + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $i + 1, $arrayEnd, true); + if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === T_DOUBLE_ARROW) { + // This is actually a key, not a standalone value + // The T_DOUBLE_ARROW will be handled in the next iteration + $i++; + + continue; + } // Find the end of this value expression (handles function calls, etc.) $valueEnd = $i; $depth = 0; @@ -487,6 +498,7 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, } $i++; } else { + // Skip the comma and continue $i++; } } diff --git a/tests/_data/ArrayDeclaration/after.php b/tests/_data/ArrayDeclaration/after.php index cb18deb..b1f92df 100644 --- a/tests/_data/ArrayDeclaration/after.php +++ b/tests/_data/ArrayDeclaration/after.php @@ -13,8 +13,7 @@ public function test(): void 'z' => 'a', ], 'c' => __FILE__, - 'd' => Xyz::class, - 'r', + 'd' => Xyz::class, 'r', 'content' => $this->getContent($tokens, $i, $tagEnd), ]; } From 84cac762cb8c86821f29d884a795ed574185e265 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sat, 9 Aug 2025 03:41:28 +0200 Subject: [PATCH 11/13] Add none option or array declaration. --- .../Formatting/ArrayDeclarationSniff.php | 33 +++++++++++++++++++ .../Formatting/ArrayDeclarationSniffTest.php | 2 +- tests/_data/ArrayDeclaration/after.php | 14 +++++++- tests/_data/ArrayDeclaration/before.php | 13 +++++++- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php index 99768db..a39e75a 100644 --- a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php +++ b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php @@ -30,6 +30,7 @@ class ArrayDeclarationSniff implements Sniff * Options: * - 'assoc' (default): Only enforce one item per line for associative arrays * - 'all': Enforce one item per line for all arrays (both associative and indexed) + * - 'none': Disable multi-line indentation checks * * @var string */ @@ -344,8 +345,16 @@ public function processMultiLineArray(File $phpcsFile, int $stackPtr, int $array protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, int $arrayEnd): void { + // Early return if mode is 'none' + if ($this->multiLineIndentationMode === 'none') { + return; + } + $tokens = $phpcsFile->getTokens(); $pairs = []; + + // Debug: identify which array we're processing + // file_put_contents('/tmp/array_debug.log', "\nProcessing array from {$tokens[$arrayStart]['line']} to {$tokens[$arrayEnd]['line']}\n", FILE_APPEND); $i = $arrayStart + 1; while ($i < $arrayEnd) { @@ -403,6 +412,18 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, continue; } + // Handle nested arrays - if we hit an array opener, skip to its closer + if ($currentToken['code'] === T_OPEN_SHORT_ARRAY && isset($currentToken['bracket_closer'])) { + $valueEnd = $currentToken['bracket_closer']; + $j = $valueEnd; + continue; + } + if ($currentToken['code'] === T_ARRAY && isset($currentToken['parenthesis_closer'])) { + $valueEnd = $currentToken['parenthesis_closer']; + $j = $valueEnd; + continue; + } + // Track parentheses depth if ($currentToken['code'] === T_OPEN_PARENTHESIS) { $depth++; @@ -465,6 +486,18 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, continue; } + // Handle nested arrays - if we hit an array opener, skip to its closer + if ($currentToken['code'] === T_OPEN_SHORT_ARRAY && isset($currentToken['bracket_closer'])) { + $valueEnd = $currentToken['bracket_closer']; + $j = $valueEnd; + continue; + } + if ($currentToken['code'] === T_ARRAY && isset($currentToken['parenthesis_closer'])) { + $valueEnd = $currentToken['parenthesis_closer']; + $j = $valueEnd; + continue; + } + // Track parentheses depth if ($currentToken['code'] === T_OPEN_PARENTHESIS) { $depth++; diff --git a/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php b/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php index 4913a36..6a4fc39 100644 --- a/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php +++ b/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php @@ -17,7 +17,7 @@ class ArrayDeclarationSniffTest extends TestCase */ public function testDocBlockConstSniffer(): void { - $this->assertSnifferFindsErrors(new ArrayDeclarationSniff(), 2); + $this->assertSnifferFindsErrors(new ArrayDeclarationSniff(), 3); } /** diff --git a/tests/_data/ArrayDeclaration/after.php b/tests/_data/ArrayDeclaration/after.php index b1f92df..3fc5e35 100644 --- a/tests/_data/ArrayDeclaration/after.php +++ b/tests/_data/ArrayDeclaration/after.php @@ -13,8 +13,20 @@ public function test(): void 'z' => 'a', ], 'c' => __FILE__, - 'd' => Xyz::class, 'r', + 'd' => Xyz::class, 'content' => $this->getContent($tokens, $i, $tagEnd), ]; + + // Test case for nested arrays - should NOT be flagged + $config = [ + 'levels' => ['notice', 'info', 'debug'], + 'other' => 'value', + ]; + + // Multiple items with nested arrays - SHOULD be flagged + $multi = [ + 'first' => ['a', 'b'], + 'second' => ['c', 'd'], + ]; } } diff --git a/tests/_data/ArrayDeclaration/before.php b/tests/_data/ArrayDeclaration/before.php index 75fecf3..786ca73 100644 --- a/tests/_data/ArrayDeclaration/before.php +++ b/tests/_data/ArrayDeclaration/before.php @@ -11,7 +11,18 @@ public function test(): void 'x' => [ 'y' => 'z', 'z' => 'a', ], - 'c' => __FILE__, 'd' => Xyz::class, 'r', 'content' => $this->getContent($tokens, $i, $tagEnd), + 'c' => __FILE__, 'd' => Xyz::class, 'content' => $this->getContent($tokens, $i, $tagEnd), + ]; + + // Test case for nested arrays - should NOT be flagged + $config = [ + 'levels' => ['notice', 'info', 'debug'], + 'other' => 'value', + ]; + + // Multiple items with nested arrays - SHOULD be flagged + $multi = [ + 'first' => ['a', 'b'], 'second' => ['c', 'd'], ]; } } From bfa356018458d3ee27b488a37f8f2de9b8c05e49 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sat, 9 Aug 2025 03:43:06 +0200 Subject: [PATCH 12/13] Add none option or array declaration. --- PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php index a39e75a..910d56f 100644 --- a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php +++ b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php @@ -352,9 +352,6 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, $tokens = $phpcsFile->getTokens(); $pairs = []; - - // Debug: identify which array we're processing - // file_put_contents('/tmp/array_debug.log', "\nProcessing array from {$tokens[$arrayStart]['line']} to {$tokens[$arrayEnd]['line']}\n", FILE_APPEND); $i = $arrayStart + 1; while ($i < $arrayEnd) { @@ -416,11 +413,13 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, if ($currentToken['code'] === T_OPEN_SHORT_ARRAY && isset($currentToken['bracket_closer'])) { $valueEnd = $currentToken['bracket_closer']; $j = $valueEnd; + continue; } if ($currentToken['code'] === T_ARRAY && isset($currentToken['parenthesis_closer'])) { $valueEnd = $currentToken['parenthesis_closer']; $j = $valueEnd; + continue; } @@ -490,11 +489,13 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, if ($currentToken['code'] === T_OPEN_SHORT_ARRAY && isset($currentToken['bracket_closer'])) { $valueEnd = $currentToken['bracket_closer']; $j = $valueEnd; + continue; } if ($currentToken['code'] === T_ARRAY && isset($currentToken['parenthesis_closer'])) { $valueEnd = $currentToken['parenthesis_closer']; $j = $valueEnd; + continue; } From 02c1c56ba205975722a56b1cfada7e20855639f9 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sat, 9 Aug 2025 03:48:56 +0200 Subject: [PATCH 13/13] Add none option or array declaration. --- PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php | 6 ++---- .../Sniffs/Formatting/ArrayDeclarationSniffTest.php | 2 +- tests/_data/ArrayDeclaration/after.php | 8 ++++++++ tests/_data/ArrayDeclaration/before.php | 7 +++++++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php index 910d56f..5279e96 100644 --- a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php +++ b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php @@ -657,10 +657,8 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, continue; } - // In 'assoc' mode, only fix associative items - if ($this->multiLineIndentationMode === 'assoc' && !$p['is_associative']) { - continue; - } + // In 'assoc' mode, when we have mixed items on a line, we need to fix all of them + // Don't skip non-associative items when they're on the same line as associative ones $targetPtr = $p['key'] ?? $p['value']; diff --git a/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php b/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php index 6a4fc39..ccd6e18 100644 --- a/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php +++ b/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php @@ -17,7 +17,7 @@ class ArrayDeclarationSniffTest extends TestCase */ public function testDocBlockConstSniffer(): void { - $this->assertSnifferFindsErrors(new ArrayDeclarationSniff(), 3); + $this->assertSnifferFindsErrors(new ArrayDeclarationSniff(), 4); } /** diff --git a/tests/_data/ArrayDeclaration/after.php b/tests/_data/ArrayDeclaration/after.php index 3fc5e35..b5b9646 100644 --- a/tests/_data/ArrayDeclaration/after.php +++ b/tests/_data/ArrayDeclaration/after.php @@ -28,5 +28,13 @@ public function test(): void 'first' => ['a', 'b'], 'second' => ['c', 'd'], ]; + + // Mixed associative and non-associative items - SHOULD be flagged + $url = [ + 'controller' => 'ControllerName', + 'action' => 'view', + $uuid, + '?' => ['pdf' => 1], + ]; } } diff --git a/tests/_data/ArrayDeclaration/before.php b/tests/_data/ArrayDeclaration/before.php index 786ca73..295d88b 100644 --- a/tests/_data/ArrayDeclaration/before.php +++ b/tests/_data/ArrayDeclaration/before.php @@ -24,5 +24,12 @@ public function test(): void $multi = [ 'first' => ['a', 'b'], 'second' => ['c', 'd'], ]; + + // Mixed associative and non-associative items - SHOULD be flagged + $url = [ + 'controller' => 'ControllerName', + 'action' => 'view', $uuid, + '?' => ['pdf' => 1], + ]; } }