@@ -903,6 +903,50 @@ protected function tokenize($string)
903903 continue ;
904904 }//end if
905905
906+ /*
907+ PHP 8.0 Attributes
908+ */
909+
910+ if (PHP_VERSION_ID < 80000
911+ && $ token [0 ] === T_COMMENT
912+ && strpos ($ token [1 ], '#[ ' ) === 0
913+ ) {
914+ $ subTokens = $ this ->parsePhpAttribute ($ tokens , $ stackPtr );
915+ if ($ subTokens !== null ) {
916+ array_splice ($ tokens , $ stackPtr , 1 , $ subTokens );
917+ $ numTokens = count ($ tokens );
918+
919+ $ tokenIsArray = true ;
920+ $ token = $ tokens [$ stackPtr ];
921+ } else {
922+ $ token [0 ] = T_ATTRIBUTE ;
923+ }
924+ }
925+
926+ if ($ tokenIsArray === true
927+ && $ token [0 ] === T_ATTRIBUTE
928+ ) {
929+ // Go looking for the close bracket.
930+ $ bracketCloser = $ this ->findCloser ($ tokens , ($ stackPtr + 1 ), '[ ' , '] ' );
931+
932+ $ newToken = [];
933+ $ newToken ['code ' ] = T_ATTRIBUTE ;
934+ $ newToken ['type ' ] = 'T_ATTRIBUTE ' ;
935+ $ newToken ['content ' ] = '#[ ' ;
936+ $ finalTokens [$ newStackPtr ] = $ newToken ;
937+
938+ $ tokens [$ bracketCloser ] = [];
939+ $ tokens [$ bracketCloser ][0 ] = T_ATTRIBUTE_END ;
940+ $ tokens [$ bracketCloser ][1 ] = '] ' ;
941+
942+ if (PHP_CODESNIFFER_VERBOSITY > 1 ) {
943+ echo "\t\t* token $ bracketCloser changed from T_CLOSE_SQUARE_BRACKET to T_ATTRIBUTE_END " .PHP_EOL ;
944+ }
945+
946+ $ newStackPtr ++;
947+ continue ;
948+ }//end if
949+
906950 /*
907951 Tokenize the parameter labels for PHP 8.0 named parameters as a special T_PARAM_NAME
908952 token and ensure that the colon after it is always T_COLON.
@@ -1845,6 +1889,7 @@ function return types. We want to keep the parenthesis map clean,
18451889 T_CLASS => true ,
18461890 T_EXTENDS => true ,
18471891 T_IMPLEMENTS => true ,
1892+ T_ATTRIBUTE => true ,
18481893 T_NEW => true ,
18491894 T_CONST => true ,
18501895 T_NS_SEPARATOR => true ,
@@ -3077,4 +3122,72 @@ public static function resolveSimpleToken($token)
30773122 }//end resolveSimpleToken()
30783123
30793124
3125+ /**
3126+ * Finds a "closer" token (closing parenthesis or square bracket for example)
3127+ * Handle parenthesis balancing while searching for closing token
3128+ *
3129+ * @param array $tokens The list of tokens to iterate searching the closing token (as returned by token_get_all)
3130+ * @param int $start The starting position
3131+ * @param string $openerChar The opening character
3132+ * @param string $closerChar The closing character
3133+ *
3134+ * @return int|null The position of the closing token, if found. NULL otherwise.
3135+ */
3136+ private function findCloser (array &$ tokens , $ start , $ openerChar , $ closerChar )
3137+ {
3138+ $ numTokens = count ($ tokens );
3139+ $ stack = [0 ];
3140+ $ closer = null ;
3141+ for ($ x = $ start ; $ x < $ numTokens ; $ x ++) {
3142+ if ($ tokens [$ x ] === $ openerChar ) {
3143+ $ stack [] = $ x ;
3144+ } else if ($ tokens [$ x ] === $ closerChar ) {
3145+ array_pop ($ stack );
3146+ if (empty ($ stack ) === true ) {
3147+ $ closer = $ x ;
3148+ break ;
3149+ }
3150+ }
3151+ }
3152+
3153+ return $ closer ;
3154+
3155+ }//end findCloser()
3156+
3157+
3158+ /**
3159+ * PHP 8 attributes parser for PHP < 8
3160+ * Handles single-line and multiline attributes.
3161+ *
3162+ * @param array $tokens The original array of tokens (as returned by token_get_all)
3163+ * @param int $stackPtr The current position in token array
3164+ *
3165+ * @return array|null The array of parsed attribute tokens
3166+ */
3167+ private function parsePhpAttribute (array &$ tokens , $ stackPtr )
3168+ {
3169+
3170+ $ token = $ tokens [$ stackPtr ];
3171+
3172+ $ commentBody = substr ($ token [1 ], 2 );
3173+ $ subTokens = @token_get_all ('<?php ' .$ commentBody );
3174+ array_splice ($ subTokens , 0 , 1 , [[T_ATTRIBUTE , '#[ ' ]]);
3175+
3176+ // Go looking for the close bracket.
3177+ $ bracketCloser = $ this ->findCloser ($ subTokens , 1 , '[ ' , '] ' );
3178+ if ($ bracketCloser === null ) {
3179+ $ bracketCloser = $ this ->findCloser ($ tokens , $ stackPtr , '[ ' , '] ' );
3180+ if ($ bracketCloser === null ) {
3181+ return null ;
3182+ }
3183+
3184+ array_splice ($ subTokens , count ($ subTokens ), 0 , array_slice ($ tokens , ($ stackPtr + 1 ), ($ bracketCloser - $ stackPtr )));
3185+ array_splice ($ tokens , ($ stackPtr + 1 ), ($ bracketCloser - $ stackPtr ));
3186+ }
3187+
3188+ return $ subTokens ;
3189+
3190+ }//end parsePhpAttribute()
3191+
3192+
30803193}//end class
0 commit comments