@@ -74,7 +74,7 @@ class ScopeIndentSniff implements Sniff
74
74
* or PHP open/close tags can escape from here and have their own
75
75
* rules elsewhere.
76
76
*
77
- * @var array< int, int|string>
77
+ * @var int[]
78
78
*/
79
79
public $ ignoreIndentationTokens = [];
80
80
@@ -126,7 +126,7 @@ public function register()
126
126
* @param int $stackPtr The position of the current token
127
127
* in the stack passed in $tokens.
128
128
*
129
- * @return void| int
129
+ * @return int
130
130
*/
131
131
public function process (File $ phpcsFile , $ stackPtr )
132
132
{
@@ -146,13 +146,13 @@ public function process(File $phpcsFile, $stackPtr)
146
146
}
147
147
}
148
148
149
- $ lastOpenTag = $ stackPtr ;
150
- $ lastCloseTag = null ;
151
- $ openScopes = [];
152
- $ adjustments = [];
153
- $ setIndents = [];
154
- $ disableExactEnd = 0 ;
155
- $ tokenIndent = 0 ;
149
+ $ lastOpenTag = $ stackPtr ;
150
+ $ lastCloseTag = null ;
151
+ $ openScopes = [];
152
+ $ adjustments = [];
153
+ $ setIndents = [];
154
+ $ disableExactStack = [] ;
155
+ $ disableExactEnd = 0 ;
156
156
157
157
$ tokens = $ phpcsFile ->getTokens ();
158
158
$ first = $ phpcsFile ->findFirstOnLine (T_INLINE_HTML , $ stackPtr );
@@ -189,6 +189,11 @@ public function process(File $phpcsFile, $stackPtr)
189
189
$ checkAnnotations = $ phpcsFile ->config ->annotations ;
190
190
191
191
for ($ i = ($ stackPtr + 1 ); $ i < $ phpcsFile ->numTokens ; $ i ++) {
192
+ if ($ i === false ) {
193
+ // Something has gone very wrong; maybe a parse error.
194
+ break ;
195
+ }
196
+
192
197
if ($ checkAnnotations === true
193
198
&& $ tokens [$ i ]['code ' ] === T_PHPCS_SET
194
199
&& isset ($ tokens [$ i ]['sniffCode ' ]) === true
@@ -232,6 +237,7 @@ public function process(File $phpcsFile, $stackPtr)
232
237
if ($ tokens [$ i ]['code ' ] === T_OPEN_PARENTHESIS
233
238
&& isset ($ tokens [$ i ]['parenthesis_closer ' ]) === true
234
239
) {
240
+ $ disableExactStack [$ tokens [$ i ]['parenthesis_closer ' ]] = $ tokens [$ i ]['parenthesis_closer ' ];
235
241
$ disableExactEnd = max ($ disableExactEnd , $ tokens [$ i ]['parenthesis_closer ' ]);
236
242
if ($ this ->debug === true ) {
237
243
$ line = $ tokens [$ i ]['line ' ];
@@ -337,7 +343,14 @@ public function process(File $phpcsFile, $stackPtr)
337
343
echo "\t* open tag is inside condition; using open tag * " .PHP_EOL ;
338
344
}
339
345
340
- $ checkIndent = ($ tokens [$ lastOpenTag ]['column ' ] - 1 );
346
+ $ first = $ phpcsFile ->findFirstOnLine ([T_WHITESPACE , T_INLINE_HTML ], $ lastOpenTag , true );
347
+ if ($ this ->debug === true ) {
348
+ $ line = $ tokens [$ first ]['line ' ];
349
+ $ type = $ tokens [$ first ]['type ' ];
350
+ echo "\t* first token on line $ line is $ first ( $ type) * " .PHP_EOL ;
351
+ }
352
+
353
+ $ checkIndent = ($ tokens [$ first ]['column ' ] - 1 );
341
354
if (isset ($ adjustments [$ condition ]) === true ) {
342
355
$ checkIndent += $ adjustments [$ condition ];
343
356
}
@@ -483,12 +496,7 @@ public function process(File $phpcsFile, $stackPtr)
483
496
484
497
$ arrayOpener = $ tokens [$ arrayCloser ]['bracket_opener ' ];
485
498
if ($ tokens [$ arrayCloser ]['line ' ] !== $ tokens [$ arrayOpener ]['line ' ]) {
486
- $ first = $ phpcsFile ->findFirstOnLine (T_WHITESPACE , $ arrayOpener , true );
487
- $ checkIndent = ($ tokens [$ first ]['column ' ] - 1 );
488
- if (isset ($ adjustments [$ first ]) === true ) {
489
- $ checkIndent += $ adjustments [$ first ];
490
- }
491
-
499
+ $ first = $ phpcsFile ->findFirstOnLine (T_WHITESPACE , $ arrayOpener , true );
492
500
$ exact = false ;
493
501
494
502
if ($ this ->debug === true ) {
@@ -524,6 +532,11 @@ public function process(File $phpcsFile, $stackPtr)
524
532
$ first = $ phpcsFile ->findNext (T_WHITESPACE , ($ first + 1 ), null , true );
525
533
}
526
534
535
+ $ checkIndent = ($ tokens [$ first ]['column ' ] - 1 );
536
+ if (isset ($ adjustments [$ first ]) === true ) {
537
+ $ checkIndent += $ adjustments [$ first ];
538
+ }
539
+
527
540
if (isset ($ tokens [$ first ]['scope_closer ' ]) === true
528
541
&& $ tokens [$ first ]['scope_closer ' ] === $ first
529
542
) {
@@ -609,11 +622,11 @@ public function process(File $phpcsFile, $stackPtr)
609
622
610
623
// Scope closers reset the required indent to the same level as the opening condition.
611
624
if (($ checkToken !== null
612
- && isset ($ openScopes [$ checkToken ]) === true
625
+ && ( isset ($ openScopes [$ checkToken ]) === true
613
626
|| (isset ($ tokens [$ checkToken ]['scope_condition ' ]) === true
614
627
&& isset ($ tokens [$ checkToken ]['scope_closer ' ]) === true
615
628
&& $ tokens [$ checkToken ]['scope_closer ' ] === $ checkToken
616
- && $ tokens [$ checkToken ]['line ' ] !== $ tokens [$ tokens [$ checkToken ]['scope_opener ' ]]['line ' ]))
629
+ && $ tokens [$ checkToken ]['line ' ] !== $ tokens [$ tokens [$ checkToken ]['scope_opener ' ]]['line ' ])))
617
630
|| ($ checkToken === null
618
631
&& isset ($ openScopes [$ i ]) === true )
619
632
) {
@@ -796,6 +809,19 @@ public function process(File $phpcsFile, $stackPtr)
796
809
) {
797
810
$ exact = true ;
798
811
812
+ if ($ disableExactEnd > $ checkToken ) {
813
+ foreach ($ disableExactStack as $ disableExactStackEnd ) {
814
+ if ($ disableExactStackEnd < $ checkToken ) {
815
+ continue ;
816
+ }
817
+
818
+ if ($ tokens [$ checkToken ]['conditions ' ] === $ tokens [$ disableExactStackEnd ]['conditions ' ]) {
819
+ $ exact = false ;
820
+ break ;
821
+ }
822
+ }
823
+ }
824
+
799
825
$ lastOpener = null ;
800
826
if (empty ($ openScopes ) === false ) {
801
827
end ($ openScopes );
@@ -844,16 +870,34 @@ public function process(File $phpcsFile, $stackPtr)
844
870
&& $ tokens [($ checkToken + 1 )]['code ' ] !== T_DOUBLE_COLON
845
871
) {
846
872
$ next = $ phpcsFile ->findNext (Tokens::$ emptyTokens , ($ checkToken + 1 ), null , true );
847
- if ($ next === false || $ tokens [$ next ]['code ' ] !== T_CLOSURE ) {
848
- if ($ this ->debug === true ) {
849
- $ line = $ tokens [$ checkToken ]['line ' ];
850
- $ type = $ tokens [$ checkToken ]['type ' ];
851
- echo "\t* method prefix ( $ type) found on line $ line; indent set to exact * " .PHP_EOL ;
873
+ if ($ next === false
874
+ || ($ tokens [$ next ]['code ' ] !== T_CLOSURE
875
+ && $ tokens [$ next ]['code ' ] !== T_VARIABLE
876
+ && $ tokens [$ next ]['code ' ] !== T_FN )
877
+ ) {
878
+ $ isMethodPrefix = true ;
879
+ if (isset ($ tokens [$ checkToken ]['nested_parenthesis ' ]) === true ) {
880
+ $ parenthesis = array_keys ($ tokens [$ checkToken ]['nested_parenthesis ' ]);
881
+ $ deepestOpen = array_pop ($ parenthesis );
882
+ if (isset ($ tokens [$ deepestOpen ]['parenthesis_owner ' ]) === true
883
+ && $ tokens [$ tokens [$ deepestOpen ]['parenthesis_owner ' ]]['code ' ] === T_FUNCTION
884
+ ) {
885
+ // This is constructor property promotion and not a method prefix.
886
+ $ isMethodPrefix = false ;
887
+ }
852
888
}
853
889
854
- $ exact = true ;
855
- }
856
- }
890
+ if ($ isMethodPrefix === true ) {
891
+ if ($ this ->debug === true ) {
892
+ $ line = $ tokens [$ checkToken ]['line ' ];
893
+ $ type = $ tokens [$ checkToken ]['type ' ];
894
+ echo "\t* method prefix ( $ type) found on line $ line; indent set to exact * " .PHP_EOL ;
895
+ }
896
+
897
+ $ exact = true ;
898
+ }
899
+ }//end if
900
+ }//end if
857
901
858
902
// JS property indentation has to be exact or else if will break
859
903
// things like function and object indentation.
@@ -890,8 +934,6 @@ public function process(File $phpcsFile, $stackPtr)
890
934
}
891
935
}
892
936
}
893
-
894
- $ checkIndent = (int ) (ceil ($ checkIndent / $ this ->indent ) * $ this ->indent );
895
937
}//end if
896
938
897
939
// Close tags needs to be indented to exact column positions.
@@ -911,7 +953,8 @@ public function process(File $phpcsFile, $stackPtr)
911
953
// Don't perform strict checking on chained method calls since they
912
954
// are often covered by custom rules.
913
955
if ($ checkToken !== null
914
- && $ tokens [$ checkToken ]['code ' ] === T_OBJECT_OPERATOR
956
+ && ($ tokens [$ checkToken ]['code ' ] === T_OBJECT_OPERATOR
957
+ || $ tokens [$ checkToken ]['code ' ] === T_NULLSAFE_OBJECT_OPERATOR )
915
958
&& $ exact === true
916
959
) {
917
960
$ exact = false ;
@@ -988,18 +1031,38 @@ public function process(File $phpcsFile, $stackPtr)
988
1031
}
989
1032
990
1033
if ($ this ->tabIndent === true ) {
991
- $ error .= '%s tabs, found %s ' ;
992
- $ data = [
993
- floor ($ checkIndent / $ this ->tabWidth ),
994
- floor ($ tokenIndent / $ this ->tabWidth ),
995
- ];
1034
+ $ expectedTabs = floor ($ checkIndent / $ this ->tabWidth );
1035
+ $ foundTabs = floor ($ tokenIndent / $ this ->tabWidth );
1036
+ $ foundSpaces = ($ tokenIndent - ($ foundTabs * $ this ->tabWidth ));
1037
+ if ($ foundSpaces > 0 ) {
1038
+ if ($ foundTabs > 0 ) {
1039
+ $ error .= '%s tabs, found %s tabs and %s spaces ' ;
1040
+ $ data = [
1041
+ $ expectedTabs ,
1042
+ $ foundTabs ,
1043
+ $ foundSpaces ,
1044
+ ];
1045
+ } else {
1046
+ $ error .= '%s tabs, found %s spaces ' ;
1047
+ $ data = [
1048
+ $ expectedTabs ,
1049
+ $ foundSpaces ,
1050
+ ];
1051
+ }
1052
+ } else {
1053
+ $ error .= '%s tabs, found %s ' ;
1054
+ $ data = [
1055
+ $ expectedTabs ,
1056
+ $ foundTabs ,
1057
+ ];
1058
+ }//end if
996
1059
} else {
997
1060
$ error .= '%s spaces, found %s ' ;
998
1061
$ data = [
999
1062
$ checkIndent ,
1000
1063
$ tokenIndent ,
1001
1064
];
1002
- }
1065
+ }//end if
1003
1066
1004
1067
if ($ this ->debug === true ) {
1005
1068
$ line = $ tokens [$ checkToken ]['line ' ];
@@ -1030,15 +1093,17 @@ public function process(File $phpcsFile, $stackPtr)
1030
1093
1031
1094
// Don't check indents exactly between arrays as they tend to have custom rules.
1032
1095
if ($ tokens [$ i ]['code ' ] === T_OPEN_SHORT_ARRAY ) {
1096
+ $ disableExactStack [$ tokens [$ i ]['bracket_closer ' ]] = $ tokens [$ i ]['bracket_closer ' ];
1033
1097
$ disableExactEnd = max ($ disableExactEnd , $ tokens [$ i ]['bracket_closer ' ]);
1034
1098
if ($ this ->debug === true ) {
1035
- $ line = $ tokens [$ i ]['line ' ];
1036
- $ type = $ tokens [$ disableExactEnd ]['type ' ];
1099
+ $ line = $ tokens [$ i ]['line ' ];
1100
+ $ type = $ tokens [$ disableExactEnd ]['type ' ];
1101
+ $ endLine = $ tokens [$ disableExactEnd ]['line ' ];
1037
1102
echo "Opening short array bracket found on line $ line " .PHP_EOL ;
1038
1103
if ($ disableExactEnd === $ tokens [$ i ]['bracket_closer ' ]) {
1039
- echo "\t=> disabling exact indent checking until $ disableExactEnd ( $ type) " .PHP_EOL ;
1104
+ echo "\t=> disabling exact indent checking until $ disableExactEnd ( $ type) on line $ endLine " .PHP_EOL ;
1040
1105
} else {
1041
- echo "\t=> continuing to disable exact indent checking until $ disableExactEnd ( $ type) " .PHP_EOL ;
1106
+ echo "\t=> continuing to disable exact indent checking until $ disableExactEnd ( $ type) on line $ endLine " .PHP_EOL ;
1042
1107
}
1043
1108
}
1044
1109
}
@@ -1050,7 +1115,6 @@ public function process(File $phpcsFile, $stackPtr)
1050
1115
) {
1051
1116
if ($ this ->debug === true ) {
1052
1117
$ line = $ tokens [$ i ]['line ' ];
1053
- $ type = $ tokens [$ disableExactEnd ]['type ' ];
1054
1118
echo "Here/nowdoc found on line $ line " .PHP_EOL ;
1055
1119
}
1056
1120
@@ -1074,8 +1138,11 @@ public function process(File $phpcsFile, $stackPtr)
1074
1138
if ($ tokens [$ i ]['code ' ] === T_CONSTANT_ENCAPSED_STRING
1075
1139
|| $ tokens [$ i ]['code ' ] === T_DOUBLE_QUOTED_STRING
1076
1140
) {
1077
- $ i = $ phpcsFile ->findNext ($ tokens [$ i ]['code ' ], ($ i + 1 ), null , true );
1078
- $ i --;
1141
+ $ nextNonTextString = $ phpcsFile ->findNext ($ tokens [$ i ]['code ' ], ($ i + 1 ), null , true );
1142
+ if ($ nextNonTextString !== false ) {
1143
+ $ i = ($ nextNonTextString - 1 );
1144
+ }
1145
+
1079
1146
continue ;
1080
1147
}
1081
1148
@@ -1162,7 +1229,7 @@ public function process(File $phpcsFile, $stackPtr)
1162
1229
if ($ this ->debug === true ) {
1163
1230
$ type = str_replace ('_ ' , ' ' , strtolower (substr ($ tokens [$ i ]['type ' ], 2 )));
1164
1231
$ line = $ tokens [$ i ]['line ' ];
1165
- echo "* ignoring single-line $ type on line $ line " .PHP_EOL ;
1232
+ echo "* ignoring single-line $ type on line $ line * " .PHP_EOL ;
1166
1233
}
1167
1234
1168
1235
$ i = $ closer ;
@@ -1232,14 +1299,24 @@ public function process(File $phpcsFile, $stackPtr)
1232
1299
if ($ this ->debug === true ) {
1233
1300
$ line = $ tokens [$ i ]['line ' ];
1234
1301
$ type = $ tokens [$ i ]['type ' ];
1235
- echo "* ignoring single-line $ type on line $ line " .PHP_EOL ;
1302
+ echo "* ignoring single-line $ type on line $ line * " .PHP_EOL ;
1236
1303
}
1237
1304
1238
1305
$ i = $ closer ;
1239
1306
continue ;
1240
1307
}
1241
1308
1242
1309
$ condition = $ tokens [$ tokens [$ i ]['scope_condition ' ]]['code ' ];
1310
+ if ($ condition === T_FN ) {
1311
+ if ($ this ->debug === true ) {
1312
+ $ line = $ tokens [$ tokens [$ i ]['scope_condition ' ]]['line ' ];
1313
+ echo "* ignoring arrow function on line $ line * " .PHP_EOL ;
1314
+ }
1315
+
1316
+ $ i = $ closer ;
1317
+ continue ;
1318
+ }
1319
+
1243
1320
if (isset (Tokens::$ scopeOpeners [$ condition ]) === true
1244
1321
&& in_array ($ condition , $ this ->nonIndentingScopes , true ) === false
1245
1322
) {
@@ -1279,7 +1356,7 @@ public function process(File $phpcsFile, $stackPtr)
1279
1356
if ($ tokens [$ i ]['line ' ] === $ tokens [$ closer ]['line ' ]) {
1280
1357
if ($ this ->debug === true ) {
1281
1358
$ line = $ tokens [$ i ]['line ' ];
1282
- echo "* ignoring single-line JS object on line $ line " .PHP_EOL ;
1359
+ echo "* ignoring single-line JS object on line $ line * " .PHP_EOL ;
1283
1360
}
1284
1361
1285
1362
$ i = $ closer ;
@@ -1309,11 +1386,14 @@ public function process(File $phpcsFile, $stackPtr)
1309
1386
continue ;
1310
1387
}//end if
1311
1388
1312
- // Closing an anon class or function.
1389
+ // Closing an anon class, closure, or match.
1390
+ // Each may be returned, which can confuse control structures that
1391
+ // use return as a closer, like CASE statements.
1313
1392
if (isset ($ tokens [$ i ]['scope_condition ' ]) === true
1314
1393
&& $ tokens [$ i ]['scope_closer ' ] === $ i
1315
1394
&& ($ tokens [$ tokens [$ i ]['scope_condition ' ]]['code ' ] === T_CLOSURE
1316
- || $ tokens [$ tokens [$ i ]['scope_condition ' ]]['code ' ] === T_ANON_CLASS )
1395
+ || $ tokens [$ tokens [$ i ]['scope_condition ' ]]['code ' ] === T_ANON_CLASS
1396
+ || $ tokens [$ tokens [$ i ]['scope_condition ' ]]['code ' ] === T_MATCH )
1317
1397
) {
1318
1398
if ($ this ->debug === true ) {
1319
1399
$ type = str_replace ('_ ' , ' ' , strtolower (substr ($ tokens [$ tokens [$ i ]['scope_condition ' ]]['type ' ], 2 )));
@@ -1493,7 +1573,7 @@ protected function adjustIndent(File $phpcsFile, $stackPtr, $length, $change)
1493
1573
$ padding = '' ;
1494
1574
if ($ length > 0 ) {
1495
1575
if ($ this ->tabIndent === true ) {
1496
- $ numTabs = ( int ) floor ($ length / $ this ->tabWidth );
1576
+ $ numTabs = floor ($ length / $ this ->tabWidth );
1497
1577
if ($ numTabs > 0 ) {
1498
1578
$ numSpaces = ($ length - ($ numTabs * $ this ->tabWidth ));
1499
1579
$ padding = str_repeat ("\t" , $ numTabs ).str_repeat (' ' , $ numSpaces );
@@ -1531,7 +1611,7 @@ protected function adjustIndent(File $phpcsFile, $stackPtr, $length, $change)
1531
1611
$ padding = ($ length + $ change );
1532
1612
if ($ padding > 0 ) {
1533
1613
if ($ this ->tabIndent === true ) {
1534
- $ numTabs = ( int ) floor ($ padding / $ this ->tabWidth );
1614
+ $ numTabs = floor ($ padding / $ this ->tabWidth );
1535
1615
$ numSpaces = ($ padding - ($ numTabs * $ this ->tabWidth ));
1536
1616
$ padding = str_repeat ("\t" , $ numTabs ).str_repeat (' ' , $ numSpaces );
1537
1617
} else {
0 commit comments