@@ -36,6 +36,26 @@ class ArrayDeclarationSniff implements Sniff
36
36
*/
37
37
public string $ multiLineIndentationMode = 'assoc ' ;
38
38
39
+ /**
40
+ * Maximum number of non-associative items allowed on a single line.
41
+ * When exceeded, items will be split to multiple lines.
42
+ * Set to 0 to disable this check.
43
+ *
44
+ * @var int
45
+ */
46
+ public int $ maxNonAssocItemsPerLine = 10 ;
47
+
48
+ /**
49
+ * Action to take when non-associative items exceed the limit.
50
+ *
51
+ * Options:
52
+ * - 'chunk' (default): Group items into chunks of maxNonAssocItemsPerLine
53
+ * - 'split': Put each item on its own line
54
+ *
55
+ * @var string
56
+ */
57
+ public string $ nonAssocExceedAction = 'chunk ' ;
58
+
39
59
/**
40
60
* @inheritDoc
41
61
*/
@@ -555,26 +575,49 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart,
555
575
556
576
// Check if we should process these items based on configuration
557
577
$ shouldProcess = false ;
558
- if ($ this ->multiLineIndentationMode === 'all ' ) {
559
- $ shouldProcess = true ;
560
- } else {
561
- // In 'assoc' mode, only process if at least one item on this line is associative
562
- foreach ($ items as $ item ) {
563
- if ($ item ['is_associative ' ]) {
564
- $ shouldProcess = true ;
578
+ $ hasAssociative = false ;
579
+ $ nonAssocCount = 0 ;
565
580
566
- break ;
567
- }
581
+ // Count associative and non-associative items
582
+ foreach ($ items as $ item ) {
583
+ if ($ item ['is_associative ' ]) {
584
+ $ hasAssociative = true ;
585
+ } else {
586
+ $ nonAssocCount ++;
568
587
}
569
588
}
570
589
590
+ if ($ this ->multiLineIndentationMode === 'all ' ) {
591
+ $ shouldProcess = true ;
592
+ } elseif ($ this ->multiLineIndentationMode === 'assoc ' && $ hasAssociative ) {
593
+ // In 'assoc' mode with associative items
594
+ $ shouldProcess = true ;
595
+ } elseif ($ this ->maxNonAssocItemsPerLine > 0 && $ nonAssocCount > $ this ->maxNonAssocItemsPerLine ) {
596
+ // Check if non-associative items exceed the limit
597
+ $ shouldProcess = true ;
598
+ }
599
+
571
600
if (!$ shouldProcess ) {
572
601
continue ;
573
602
}
574
603
575
604
foreach ($ items as $ i => $ pair ) {
576
- // In 'assoc' mode, only flag associative items
577
- if ($ this ->multiLineIndentationMode === 'assoc ' && !$ pair ['is_associative ' ]) {
605
+ // Determine if this item should be flagged
606
+ $ shouldFlag = false ;
607
+
608
+ if ($ this ->multiLineIndentationMode === 'all ' ) {
609
+ $ shouldFlag = true ;
610
+ } elseif ($ this ->multiLineIndentationMode === 'assoc ' && $ hasAssociative ) {
611
+ // In 'assoc' mode with mixed items, only skip non-associative if no limit exceeded
612
+ if ($ pair ['is_associative ' ] || ($ this ->maxNonAssocItemsPerLine > 0 && $ nonAssocCount > $ this ->maxNonAssocItemsPerLine )) {
613
+ $ shouldFlag = true ;
614
+ }
615
+ } elseif ($ this ->maxNonAssocItemsPerLine > 0 && $ nonAssocCount > $ this ->maxNonAssocItemsPerLine ) {
616
+ // Non-associative array exceeds limit
617
+ $ shouldFlag = true ;
618
+ }
619
+
620
+ if (!$ shouldFlag ) {
578
621
continue ;
579
622
}
580
623
@@ -652,26 +695,53 @@ protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart,
652
695
}
653
696
654
697
$ phpcsFile ->fixer ->beginChangeset ();
698
+
699
+ // Determine if we should use chunking for this line
700
+ $ useChunking = false ;
701
+ if (
702
+ $ this ->nonAssocExceedAction === 'chunk ' &&
703
+ $ this ->maxNonAssocItemsPerLine > 0 &&
704
+ $ nonAssocCount > $ this ->maxNonAssocItemsPerLine &&
705
+ !$ hasAssociative
706
+ ) {
707
+ // Use chunking only for pure non-associative arrays
708
+ $ useChunking = true ;
709
+ }
710
+
711
+ $ nonAssocIndex = 0 ;
655
712
foreach ($ items as $ j => $ p ) {
656
713
if ($ j === 0 ) {
657
714
continue ;
658
715
}
659
716
660
- // In 'assoc' mode, when we have mixed items on a line, we need to fix all of them
661
- // Don't skip non-associative items when they're on the same line as associative ones
662
-
663
717
$ targetPtr = $ p ['key ' ] ?? $ p ['value ' ];
664
718
665
- // Find any whitespace before the target token and remove it
666
- $ prevToken = $ targetPtr - 1 ;
667
- while ($ prevToken >= $ arrayStart && in_array ($ tokens [$ prevToken ]['code ' ], [T_WHITESPACE , T_COMMA ], true )) {
668
- if ($ tokens [$ prevToken ]['code ' ] === T_WHITESPACE ) {
669
- $ phpcsFile ->fixer ->replaceToken ($ prevToken , '' );
719
+ // Determine if we should add a newline before this item
720
+ $ shouldAddNewline = false ;
721
+
722
+ if ($ useChunking && !$ p ['is_associative ' ]) {
723
+ // For chunking non-associative items
724
+ $ nonAssocIndex ++;
725
+ if ($ nonAssocIndex % $ this ->maxNonAssocItemsPerLine === 0 ) {
726
+ $ shouldAddNewline = true ;
670
727
}
671
- $ prevToken --;
728
+ } else {
729
+ // Default behavior: newline before each item (except first)
730
+ $ shouldAddNewline = true ;
672
731
}
673
732
674
- $ phpcsFile ->fixer ->addContentBefore ($ targetPtr , "\n" . $ baseIndent );
733
+ if ($ shouldAddNewline ) {
734
+ // Find any whitespace before the target token and remove it
735
+ $ prevToken = $ targetPtr - 1 ;
736
+ while ($ prevToken >= $ arrayStart && in_array ($ tokens [$ prevToken ]['code ' ], [T_WHITESPACE , T_COMMA ], true )) {
737
+ if ($ tokens [$ prevToken ]['code ' ] === T_WHITESPACE ) {
738
+ $ phpcsFile ->fixer ->replaceToken ($ prevToken , '' );
739
+ }
740
+ $ prevToken --;
741
+ }
742
+
743
+ $ phpcsFile ->fixer ->addContentBefore ($ targetPtr , "\n" . $ baseIndent );
744
+ }
675
745
}
676
746
$ phpcsFile ->fixer ->endChangeset ();
677
747
}
0 commit comments