@@ -893,6 +893,62 @@ protected function tokenize($string)
893893 continue ;
894894 }//end if
895895
896+ /*
897+ Tokenize the parameter labels for PHP 8.0 named parameters as a special T_PARAM_NAME
898+ token and ensure that the colon after it is always T_COLON.
899+ */
900+
901+ if ($ tokenIsArray === true
902+ && preg_match ('`^[a-zA-Z_\x80-\xff]` ' , $ token [1 ]) === 1
903+ ) {
904+ // Get the next non-empty token.
905+ for ($ i = ($ stackPtr + 1 ); $ i < $ numTokens ; $ i ++) {
906+ if (is_array ($ tokens [$ i ]) === false
907+ || isset (Util \Tokens::$ emptyTokens [$ tokens [$ i ][0 ]]) === false
908+ ) {
909+ break ;
910+ }
911+ }
912+
913+ if (isset ($ tokens [$ i ]) === true
914+ && is_array ($ tokens [$ i ]) === false
915+ && $ tokens [$ i ] === ': '
916+ ) {
917+ // Get the previous non-empty token.
918+ for ($ j = ($ stackPtr - 1 ); $ j > 0 ; $ j --) {
919+ if (is_array ($ tokens [$ j ]) === false
920+ || isset (Util \Tokens::$ emptyTokens [$ tokens [$ j ][0 ]]) === false
921+ ) {
922+ break ;
923+ }
924+ }
925+
926+ if (is_array ($ tokens [$ j ]) === false
927+ && ($ tokens [$ j ] === '( '
928+ || $ tokens [$ j ] === ', ' )
929+ ) {
930+ $ newToken = [];
931+ $ newToken ['code ' ] = T_PARAM_NAME ;
932+ $ newToken ['type ' ] = 'T_PARAM_NAME ' ;
933+ $ newToken ['content ' ] = $ token [1 ];
934+ $ finalTokens [$ newStackPtr ] = $ newToken ;
935+
936+ $ newStackPtr ++;
937+
938+ // Modify the original token stack so that future checks, like
939+ // determining T_COLON vs T_INLINE_ELSE can handle this correctly.
940+ $ tokens [$ stackPtr ][0 ] = T_PARAM_NAME ;
941+
942+ if (PHP_CODESNIFFER_VERBOSITY > 1 ) {
943+ $ type = Util \Tokens::tokenName ($ token [0 ]);
944+ echo "\t\t* token $ stackPtr changed from $ type to T_PARAM_NAME " .PHP_EOL ;
945+ }
946+
947+ continue ;
948+ }
949+ }//end if
950+ }//end if
951+
896952 /*
897953 Before PHP 7.0, the "yield from" was tokenized as
898954 T_YIELD, T_WHITESPACE and T_STRING. So look for
@@ -1701,76 +1757,98 @@ function return types. We want to keep the parenthesis map clean,
17011757 // Convert colons that are actually the ELSE component of an
17021758 // inline IF statement.
17031759 if (empty ($ insideInlineIf ) === false && $ newToken ['code ' ] === T_COLON ) {
1704- // Make sure this isn't a return type separator.
17051760 $ isInlineIf = true ;
1761+
1762+ // Make sure this isn't a named parameter label.
1763+ // Get the previous non-empty token.
17061764 for ($ i = ($ stackPtr - 1 ); $ i > 0 ; $ i --) {
17071765 if (is_array ($ tokens [$ i ]) === false
1708- || ($ tokens [$ i ][0 ] !== T_DOC_COMMENT
1709- && $ tokens [$ i ][0 ] !== T_COMMENT
1710- && $ tokens [$ i ][0 ] !== T_WHITESPACE )
1766+ || isset (Util \Tokens::$ emptyTokens [$ tokens [$ i ][0 ]]) === false
17111767 ) {
17121768 break ;
17131769 }
17141770 }
17151771
1716- if ($ tokens [$ i ] === ') ' ) {
1717- $ parenCount = 1 ;
1718- for ($ i --; $ i > 0 ; $ i --) {
1719- if ($ tokens [$ i ] === '( ' ) {
1720- $ parenCount --;
1721- if ($ parenCount === 0 ) {
1722- break ;
1723- }
1724- } else if ($ tokens [$ i ] === ') ' ) {
1725- $ parenCount ++;
1726- }
1772+ if ($ tokens [$ i ][0 ] === T_PARAM_NAME ) {
1773+ $ isInlineIf = false ;
1774+ if (PHP_CODESNIFFER_VERBOSITY > 1 ) {
1775+ echo "\t\t* token is parameter label, not T_INLINE_ELSE " .PHP_EOL ;
17271776 }
1777+ }
17281778
1729- // We've found the open parenthesis, so if the previous
1730- // non-empty token is FUNCTION or USE, this is a return type.
1731- // Note that we need to skip T_STRING tokens here as these
1732- // can be function names.
1733- for ($ i --; $ i > 0 ; $ i --) {
1779+ if ($ isInlineIf === true ) {
1780+ // Make sure this isn't a return type separator.
1781+ for ($ i = ($ stackPtr - 1 ); $ i > 0 ; $ i --) {
17341782 if (is_array ($ tokens [$ i ]) === false
17351783 || ($ tokens [$ i ][0 ] !== T_DOC_COMMENT
17361784 && $ tokens [$ i ][0 ] !== T_COMMENT
1737- && $ tokens [$ i ][0 ] !== T_WHITESPACE
1738- && $ tokens [$ i ][0 ] !== T_STRING )
1785+ && $ tokens [$ i ][0 ] !== T_WHITESPACE )
17391786 ) {
17401787 break ;
17411788 }
17421789 }
17431790
1744- if ($ tokens [$ i ][0 ] === T_FUNCTION || $ tokens [$ i ][0 ] === T_FN || $ tokens [$ i ][0 ] === T_USE ) {
1745- $ isInlineIf = false ;
1746- if (PHP_CODESNIFFER_VERBOSITY > 1 ) {
1747- echo "\t\t* token is return type, not T_INLINE_ELSE " .PHP_EOL ;
1791+ if ($ tokens [$ i ] === ') ' ) {
1792+ $ parenCount = 1 ;
1793+ for ($ i --; $ i > 0 ; $ i --) {
1794+ if ($ tokens [$ i ] === '( ' ) {
1795+ $ parenCount --;
1796+ if ($ parenCount === 0 ) {
1797+ break ;
1798+ }
1799+ } else if ($ tokens [$ i ] === ') ' ) {
1800+ $ parenCount ++;
1801+ }
17481802 }
1749- }
1803+
1804+ // We've found the open parenthesis, so if the previous
1805+ // non-empty token is FUNCTION or USE, this is a return type.
1806+ // Note that we need to skip T_STRING tokens here as these
1807+ // can be function names.
1808+ for ($ i --; $ i > 0 ; $ i --) {
1809+ if (is_array ($ tokens [$ i ]) === false
1810+ || ($ tokens [$ i ][0 ] !== T_DOC_COMMENT
1811+ && $ tokens [$ i ][0 ] !== T_COMMENT
1812+ && $ tokens [$ i ][0 ] !== T_WHITESPACE
1813+ && $ tokens [$ i ][0 ] !== T_STRING )
1814+ ) {
1815+ break ;
1816+ }
1817+ }
1818+
1819+ if ($ tokens [$ i ][0 ] === T_FUNCTION || $ tokens [$ i ][0 ] === T_FN || $ tokens [$ i ][0 ] === T_USE ) {
1820+ $ isInlineIf = false ;
1821+ if (PHP_CODESNIFFER_VERBOSITY > 1 ) {
1822+ echo "\t\t* token is return type, not T_INLINE_ELSE " .PHP_EOL ;
1823+ }
1824+ }
1825+ }//end if
17501826 }//end if
17511827
17521828 // Check to see if this is a CASE or DEFAULT opener.
1753- $ inlineIfToken = $ insideInlineIf [(count ($ insideInlineIf ) - 1 )];
1754- for ($ i = $ stackPtr ; $ i > $ inlineIfToken ; $ i --) {
1755- if (is_array ($ tokens [$ i ]) === true
1756- && ($ tokens [$ i ][0 ] === T_CASE
1757- || $ tokens [$ i ][0 ] === T_DEFAULT )
1758- ) {
1759- $ isInlineIf = false ;
1760- if (PHP_CODESNIFFER_VERBOSITY > 1 ) {
1761- echo "\t\t* token is T_CASE or T_DEFAULT opener, not T_INLINE_ELSE " .PHP_EOL ;
1762- }
1829+ if ($ isInlineIf === true ) {
1830+ $ inlineIfToken = $ insideInlineIf [(count ($ insideInlineIf ) - 1 )];
1831+ for ($ i = $ stackPtr ; $ i > $ inlineIfToken ; $ i --) {
1832+ if (is_array ($ tokens [$ i ]) === true
1833+ && ($ tokens [$ i ][0 ] === T_CASE
1834+ || $ tokens [$ i ][0 ] === T_DEFAULT )
1835+ ) {
1836+ $ isInlineIf = false ;
1837+ if (PHP_CODESNIFFER_VERBOSITY > 1 ) {
1838+ echo "\t\t* token is T_CASE or T_DEFAULT opener, not T_INLINE_ELSE " .PHP_EOL ;
1839+ }
17631840
1764- break ;
1765- }
1841+ break ;
1842+ }
17661843
1767- if (is_array ($ tokens [$ i ]) === false
1768- && ($ tokens [$ i ] === '; '
1769- || $ tokens [$ i ] === '{ ' )
1770- ) {
1771- break ;
1844+ if (is_array ($ tokens [$ i ]) === false
1845+ && ($ tokens [$ i ] === '; '
1846+ || $ tokens [$ i ] === '{ ' )
1847+ ) {
1848+ break ;
1849+ }
17721850 }
1773- }
1851+ }//end if
17741852
17751853 if ($ isInlineIf === true ) {
17761854 array_pop ($ insideInlineIf );
0 commit comments