@@ -74,7 +74,7 @@ class ScopeIndentSniff implements Sniff
7474 * or PHP open/close tags can escape from here and have their own
7575 * rules elsewhere.
7676 *
77- * @var array< int, int|string>
77+ * @var int[]
7878 */
7979 public $ ignoreIndentationTokens = [];
8080
@@ -126,7 +126,7 @@ public function register()
126126 * @param int $stackPtr The position of the current token
127127 * in the stack passed in $tokens.
128128 *
129- * @return void| int
129+ * @return int
130130 */
131131 public function process (File $ phpcsFile , $ stackPtr )
132132 {
@@ -146,13 +146,13 @@ public function process(File $phpcsFile, $stackPtr)
146146 }
147147 }
148148
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 ;
156156
157157 $ tokens = $ phpcsFile ->getTokens ();
158158 $ first = $ phpcsFile ->findFirstOnLine (T_INLINE_HTML , $ stackPtr );
@@ -189,6 +189,11 @@ public function process(File $phpcsFile, $stackPtr)
189189 $ checkAnnotations = $ phpcsFile ->config ->annotations ;
190190
191191 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+
192197 if ($ checkAnnotations === true
193198 && $ tokens [$ i ]['code ' ] === T_PHPCS_SET
194199 && isset ($ tokens [$ i ]['sniffCode ' ]) === true
@@ -232,6 +237,7 @@ public function process(File $phpcsFile, $stackPtr)
232237 if ($ tokens [$ i ]['code ' ] === T_OPEN_PARENTHESIS
233238 && isset ($ tokens [$ i ]['parenthesis_closer ' ]) === true
234239 ) {
240+ $ disableExactStack [$ tokens [$ i ]['parenthesis_closer ' ]] = $ tokens [$ i ]['parenthesis_closer ' ];
235241 $ disableExactEnd = max ($ disableExactEnd , $ tokens [$ i ]['parenthesis_closer ' ]);
236242 if ($ this ->debug === true ) {
237243 $ line = $ tokens [$ i ]['line ' ];
@@ -337,7 +343,14 @@ public function process(File $phpcsFile, $stackPtr)
337343 echo "\t* open tag is inside condition; using open tag * " .PHP_EOL ;
338344 }
339345
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 );
341354 if (isset ($ adjustments [$ condition ]) === true ) {
342355 $ checkIndent += $ adjustments [$ condition ];
343356 }
@@ -483,12 +496,7 @@ public function process(File $phpcsFile, $stackPtr)
483496
484497 $ arrayOpener = $ tokens [$ arrayCloser ]['bracket_opener ' ];
485498 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 );
492500 $ exact = false ;
493501
494502 if ($ this ->debug === true ) {
@@ -524,6 +532,11 @@ public function process(File $phpcsFile, $stackPtr)
524532 $ first = $ phpcsFile ->findNext (T_WHITESPACE , ($ first + 1 ), null , true );
525533 }
526534
535+ $ checkIndent = ($ tokens [$ first ]['column ' ] - 1 );
536+ if (isset ($ adjustments [$ first ]) === true ) {
537+ $ checkIndent += $ adjustments [$ first ];
538+ }
539+
527540 if (isset ($ tokens [$ first ]['scope_closer ' ]) === true
528541 && $ tokens [$ first ]['scope_closer ' ] === $ first
529542 ) {
@@ -609,11 +622,11 @@ public function process(File $phpcsFile, $stackPtr)
609622
610623 // Scope closers reset the required indent to the same level as the opening condition.
611624 if (($ checkToken !== null
612- && isset ($ openScopes [$ checkToken ]) === true
625+ && ( isset ($ openScopes [$ checkToken ]) === true
613626 || (isset ($ tokens [$ checkToken ]['scope_condition ' ]) === true
614627 && isset ($ tokens [$ checkToken ]['scope_closer ' ]) === true
615628 && $ 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 ' ])))
617630 || ($ checkToken === null
618631 && isset ($ openScopes [$ i ]) === true )
619632 ) {
@@ -796,6 +809,19 @@ public function process(File $phpcsFile, $stackPtr)
796809 ) {
797810 $ exact = true ;
798811
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+
799825 $ lastOpener = null ;
800826 if (empty ($ openScopes ) === false ) {
801827 end ($ openScopes );
@@ -844,16 +870,34 @@ public function process(File $phpcsFile, $stackPtr)
844870 && $ tokens [($ checkToken + 1 )]['code ' ] !== T_DOUBLE_COLON
845871 ) {
846872 $ 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+ }
852888 }
853889
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
857901
858902 // JS property indentation has to be exact or else if will break
859903 // things like function and object indentation.
@@ -890,8 +934,6 @@ public function process(File $phpcsFile, $stackPtr)
890934 }
891935 }
892936 }
893-
894- $ checkIndent = (int ) (ceil ($ checkIndent / $ this ->indent ) * $ this ->indent );
895937 }//end if
896938
897939 // Close tags needs to be indented to exact column positions.
@@ -911,7 +953,8 @@ public function process(File $phpcsFile, $stackPtr)
911953 // Don't perform strict checking on chained method calls since they
912954 // are often covered by custom rules.
913955 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 )
915958 && $ exact === true
916959 ) {
917960 $ exact = false ;
@@ -988,18 +1031,38 @@ public function process(File $phpcsFile, $stackPtr)
9881031 }
9891032
9901033 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
9961059 } else {
9971060 $ error .= '%s spaces, found %s ' ;
9981061 $ data = [
9991062 $ checkIndent ,
10001063 $ tokenIndent ,
10011064 ];
1002- }
1065+ }//end if
10031066
10041067 if ($ this ->debug === true ) {
10051068 $ line = $ tokens [$ checkToken ]['line ' ];
@@ -1030,15 +1093,17 @@ public function process(File $phpcsFile, $stackPtr)
10301093
10311094 // Don't check indents exactly between arrays as they tend to have custom rules.
10321095 if ($ tokens [$ i ]['code ' ] === T_OPEN_SHORT_ARRAY ) {
1096+ $ disableExactStack [$ tokens [$ i ]['bracket_closer ' ]] = $ tokens [$ i ]['bracket_closer ' ];
10331097 $ disableExactEnd = max ($ disableExactEnd , $ tokens [$ i ]['bracket_closer ' ]);
10341098 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 ' ];
10371102 echo "Opening short array bracket found on line $ line " .PHP_EOL ;
10381103 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 ;
10401105 } 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 ;
10421107 }
10431108 }
10441109 }
@@ -1050,7 +1115,6 @@ public function process(File $phpcsFile, $stackPtr)
10501115 ) {
10511116 if ($ this ->debug === true ) {
10521117 $ line = $ tokens [$ i ]['line ' ];
1053- $ type = $ tokens [$ disableExactEnd ]['type ' ];
10541118 echo "Here/nowdoc found on line $ line " .PHP_EOL ;
10551119 }
10561120
@@ -1074,8 +1138,11 @@ public function process(File $phpcsFile, $stackPtr)
10741138 if ($ tokens [$ i ]['code ' ] === T_CONSTANT_ENCAPSED_STRING
10751139 || $ tokens [$ i ]['code ' ] === T_DOUBLE_QUOTED_STRING
10761140 ) {
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+
10791146 continue ;
10801147 }
10811148
@@ -1162,7 +1229,7 @@ public function process(File $phpcsFile, $stackPtr)
11621229 if ($ this ->debug === true ) {
11631230 $ type = str_replace ('_ ' , ' ' , strtolower (substr ($ tokens [$ i ]['type ' ], 2 )));
11641231 $ 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 ;
11661233 }
11671234
11681235 $ i = $ closer ;
@@ -1232,14 +1299,24 @@ public function process(File $phpcsFile, $stackPtr)
12321299 if ($ this ->debug === true ) {
12331300 $ line = $ tokens [$ i ]['line ' ];
12341301 $ 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 ;
12361303 }
12371304
12381305 $ i = $ closer ;
12391306 continue ;
12401307 }
12411308
12421309 $ 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+
12431320 if (isset (Tokens::$ scopeOpeners [$ condition ]) === true
12441321 && in_array ($ condition , $ this ->nonIndentingScopes , true ) === false
12451322 ) {
@@ -1279,7 +1356,7 @@ public function process(File $phpcsFile, $stackPtr)
12791356 if ($ tokens [$ i ]['line ' ] === $ tokens [$ closer ]['line ' ]) {
12801357 if ($ this ->debug === true ) {
12811358 $ 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 ;
12831360 }
12841361
12851362 $ i = $ closer ;
@@ -1309,11 +1386,14 @@ public function process(File $phpcsFile, $stackPtr)
13091386 continue ;
13101387 }//end if
13111388
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.
13131392 if (isset ($ tokens [$ i ]['scope_condition ' ]) === true
13141393 && $ tokens [$ i ]['scope_closer ' ] === $ i
13151394 && ($ 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 )
13171397 ) {
13181398 if ($ this ->debug === true ) {
13191399 $ type = str_replace ('_ ' , ' ' , strtolower (substr ($ tokens [$ tokens [$ i ]['scope_condition ' ]]['type ' ], 2 )));
@@ -1493,7 +1573,7 @@ protected function adjustIndent(File $phpcsFile, $stackPtr, $length, $change)
14931573 $ padding = '' ;
14941574 if ($ length > 0 ) {
14951575 if ($ this ->tabIndent === true ) {
1496- $ numTabs = ( int ) floor ($ length / $ this ->tabWidth );
1576+ $ numTabs = floor ($ length / $ this ->tabWidth );
14971577 if ($ numTabs > 0 ) {
14981578 $ numSpaces = ($ length - ($ numTabs * $ this ->tabWidth ));
14991579 $ padding = str_repeat ("\t" , $ numTabs ).str_repeat (' ' , $ numSpaces );
@@ -1531,7 +1611,7 @@ protected function adjustIndent(File $phpcsFile, $stackPtr, $length, $change)
15311611 $ padding = ($ length + $ change );
15321612 if ($ padding > 0 ) {
15331613 if ($ this ->tabIndent === true ) {
1534- $ numTabs = ( int ) floor ($ padding / $ this ->tabWidth );
1614+ $ numTabs = floor ($ padding / $ this ->tabWidth );
15351615 $ numSpaces = ($ padding - ($ numTabs * $ this ->tabWidth ));
15361616 $ padding = str_repeat ("\t" , $ numTabs ).str_repeat (' ' , $ numSpaces );
15371617 } else {
0 commit comments