3838use PHP_CodeSniffer \Files \File ;
3939use PHP_CodeSniffer \Util \Tokens ;
4040use PHPCSUtils \BackCompat \BCTokens ;
41+ use PHPCSUtils \BackCompat \Helper ;
4142use PHPCSUtils \Tokens \Collections ;
43+ use PHPCSUtils \Utils \FunctionDeclarations ;
4244
4345/**
4446 * PHPCS native utility functions.
@@ -94,6 +96,8 @@ class BCFile
9496 * - PHPCS 3.0.0: Added support for ES6 class/method syntax.
9597 * - PHPCS 3.0.0: The Exception thrown changed from a `PHP_CodeSniffer_Exception` to
9698 * `\PHP_CodeSniffer\Exceptions\RuntimeException`.
99+ * - PHPCS 3.5.3: Allow for functions to be called `fn` for backwards compatibility.
100+ * Related to PHP 7.4 T_FN arrow functions.
97101 *
98102 * Note: For ES6 classes in combination with PHPCS 2.x, passing a `T_STRING` token to
99103 * this method will be accepted for JS files.
@@ -103,6 +107,7 @@ class BCFile
103107 * @see \PHPCSUtils\Utils\ObjectDeclarations::getName() PHPCSUtils native improved version.
104108 *
105109 * @since 1.0.0
110+ * @since 1.0.0-alpha2 Added BC support for PHP 7.4 arrow functions.
106111 *
107112 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
108113 * @param int $stackPtr The position of the declaration token
@@ -153,7 +158,9 @@ public static function getDeclarationName(File $phpcsFile, $stackPtr)
153158
154159 $ content = null ;
155160 for ($ i = ($ stackPtr + 1 ); $ i < $ phpcsFile ->numTokens ; $ i ++) {
156- if ($ tokens [$ i ]['code ' ] === T_STRING ) {
161+ if ($ tokens [$ i ]['code ' ] === T_STRING
162+ || $ tokens [$ i ]['type ' ] === 'T_FN '
163+ ) {
157164 /*
158165 * BC: In PHPCS 2.6.0, in case of live coding, the last token in a file will be tokenized
159166 * as T_STRING, but won't have the `content` index set.
@@ -238,11 +245,13 @@ public static function getDeclarationName(File $phpcsFile, $stackPtr)
238245 * be set in a "variadic_token" array index.
239246 * - PHPCS 3.5.3: Fixed a bug where the "type_hint_end_token" array index for a type hinted
240247 * parameter would bleed through to the next (non-type hinted) parameter.
248+ * - PHPCS 3.5.3: Added support for PHP 7.4 T_FN arrow functions.
241249 *
242250 * @see \PHP_CodeSniffer\Files\File::getMethodParameters() Original source.
243251 * @see \PHPCSUtils\Utils\FunctionDeclarations::getParameters() PHPCSUtils native improved version.
244252 *
245253 * @since 1.0.0
254+ * @since 1.0.0-alpha2 Added BC support for PHP 7.4 arrow functions.
246255 *
247256 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
248257 * @param int $stackPtr The position in the stack of the function token
@@ -251,7 +260,8 @@ public static function getDeclarationName(File $phpcsFile, $stackPtr)
251260 * @return array
252261 *
253262 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is not of
254- * type T_FUNCTION, T_CLOSURE, or T_USE.
263+ * type T_FUNCTION, T_CLOSURE, T_USE,
264+ * or T_FN.
255265 */
256266 public static function getMethodParameters (File $ phpcsFile , $ stackPtr )
257267 {
@@ -260,15 +270,25 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
260270 if ($ tokens [$ stackPtr ]['code ' ] !== T_FUNCTION
261271 && $ tokens [$ stackPtr ]['code ' ] !== T_CLOSURE
262272 && $ tokens [$ stackPtr ]['code ' ] !== T_USE
273+ && FunctionDeclarations::isArrowFunction ($ phpcsFile , $ stackPtr ) === false
263274 ) {
264- throw new RuntimeException ('$stackPtr must be of type T_FUNCTION or T_CLOSURE or T_USE ' );
275+ throw new RuntimeException ('$stackPtr must be of type T_FUNCTION or T_CLOSURE or T_USE or T_FN ' );
265276 }
266277
267278 if ($ tokens [$ stackPtr ]['code ' ] === T_USE ) {
268279 $ opener = $ phpcsFile ->findNext (T_OPEN_PARENTHESIS , ($ stackPtr + 1 ));
269280 if ($ opener === false || isset ($ tokens [$ opener ]['parenthesis_owner ' ]) === true ) {
270281 throw new RuntimeException ('$stackPtr was not a valid T_USE ' );
271282 }
283+ } elseif ($ tokens [$ stackPtr ]['code ' ] === \T_STRING || $ tokens [$ stackPtr ]['type ' ] === 'T_FN ' ) {
284+ /*
285+ * Arrow function in combination with PHP < 7.4 or PHPCS < 3.5.3.
286+ */
287+ $ opener = $ phpcsFile ->findNext ((Tokens::$ emptyTokens + [\T_BITWISE_AND ]), ($ stackPtr + 1 ), null , true );
288+ if ($ opener === false || $ tokens [$ opener ]['code ' ] !== T_OPEN_PARENTHESIS ) {
289+ // Live coding or syntax error, so no params to find.
290+ return [];
291+ }
272292 } else {
273293 if (isset ($ tokens [$ stackPtr ]['parenthesis_opener ' ]) === false ) {
274294 // Live coding or syntax error, so no params to find.
@@ -508,11 +528,13 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
508528 * or `true` otherwise.
509529 * - PHPCS 3.5.0: The Exception thrown changed from a `TokenizerException` to
510530 * `\PHP_CodeSniffer\Exceptions\RuntimeException`.
531+ * - PHPCS 3.5.3: Added support for PHP 7.4 T_FN arrow functions.
511532 *
512533 * @see \PHP_CodeSniffer\Files\File::getMethodProperties() Original source.
513534 * @see \PHPCSUtils\Utils\FunctionDeclarations::getProperties() PHPCSUtils native improved version.
514535 *
515536 * @since 1.0.0
537+ * @since 1.0.0-alpha2 Added BC support for PHP 7.4 arrow functions.
516538 *
517539 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
518540 * @param int $stackPtr The position in the stack of the function token to
@@ -521,16 +543,18 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
521543 * @return array
522544 *
523545 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
524- * T_FUNCTION or a T_CLOSURE token.
546+ * T_FUNCTION, T_CLOSURE, or T_FN token.
525547 */
526548 public static function getMethodProperties (File $ phpcsFile , $ stackPtr )
527549 {
528- $ tokens = $ phpcsFile ->getTokens ();
550+ $ tokens = $ phpcsFile ->getTokens ();
551+ $ arrowOpenClose = FunctionDeclarations::getArrowFunctionOpenClose ($ phpcsFile , $ stackPtr );
529552
530553 if ($ tokens [$ stackPtr ]['code ' ] !== T_FUNCTION
531554 && $ tokens [$ stackPtr ]['code ' ] !== T_CLOSURE
555+ && $ arrowOpenClose === []
532556 ) {
533- throw new RuntimeException ('$stackPtr must be of type T_FUNCTION or T_CLOSURE ' );
557+ throw new RuntimeException ('$stackPtr must be of type T_FUNCTION or T_CLOSURE or T_FN ' );
534558 }
535559
536560 if ($ tokens [$ stackPtr ]['code ' ] === T_FUNCTION ) {
@@ -595,13 +619,24 @@ public static function getMethodProperties(File $phpcsFile, $stackPtr)
595619 $ nullableReturnType = false ;
596620 $ hasBody = true ;
597621
622+ $ parenthesisCloser = null ;
598623 if (isset ($ tokens [$ stackPtr ]['parenthesis_closer ' ]) === true ) {
624+ $ parenthesisCloser = $ tokens [$ stackPtr ]['parenthesis_closer ' ];
625+ } elseif ($ arrowOpenClose !== [] && $ arrowOpenClose ['parenthesis_closer ' ] !== false ) {
626+ // Arrow function in combination with PHP < 7.4 or PHPCS < 3.5.3.
627+ $ parenthesisCloser = $ arrowOpenClose ['parenthesis_closer ' ];
628+ }
629+
630+ if (isset ($ parenthesisCloser ) === true ) {
599631 $ scopeOpener = null ;
600632 if (isset ($ tokens [$ stackPtr ]['scope_opener ' ]) === true ) {
601633 $ scopeOpener = $ tokens [$ stackPtr ]['scope_opener ' ];
634+ } elseif ($ arrowOpenClose !== [] && $ arrowOpenClose ['scope_opener ' ] !== false ) {
635+ // Arrow function in combination with PHP < 7.4 or PHPCS < 3.5.3.
636+ $ scopeOpener = $ arrowOpenClose ['scope_opener ' ];
602637 }
603638
604- for ($ i = $ tokens [ $ stackPtr ][ ' parenthesis_closer ' ] ; $ i < $ phpcsFile ->numTokens ; $ i ++) {
639+ for ($ i = $ parenthesisCloser ; $ i < $ phpcsFile ->numTokens ; $ i ++) {
605640 if (($ scopeOpener === null && $ tokens [$ i ]['code ' ] === T_SEMICOLON )
606641 || ($ scopeOpener !== null && $ i === $ scopeOpener )
607642 ) {
@@ -612,6 +647,9 @@ public static function getMethodProperties(File $phpcsFile, $stackPtr)
612647 if ($ tokens [$ i ]['type ' ] === 'T_NULLABLE '
613648 // Handle nullable tokens in PHPCS < 2.8.0.
614649 || (defined ('T_NULLABLE ' ) === false && $ tokens [$ i ]['code ' ] === T_INLINE_THEN )
650+ // Handle nullable tokens with arrow functions in PHPCS 2.8.0 - 2.9.0.
651+ || ($ arrowOpenClose !== [] && $ tokens [$ i ]['code ' ] === T_INLINE_THEN
652+ && version_compare (Helper::getVersion (), '2.9.1 ' , '< ' ) === true )
615653 ) {
616654 $ nullableReturnType = true ;
617655 }
@@ -625,8 +663,17 @@ public static function getMethodProperties(File $phpcsFile, $stackPtr)
625663 }
626664 }
627665
628- $ end = $ phpcsFile ->findNext ([T_OPEN_CURLY_BRACKET , T_SEMICOLON ], $ tokens [$ stackPtr ]['parenthesis_closer ' ]);
629- $ hasBody = $ tokens [$ end ]['code ' ] === T_OPEN_CURLY_BRACKET ;
666+ $ bodyTokens = [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET ];
667+ if ($ arrowOpenClose !== []) {
668+ $ bodyTokens = [T_DOUBLE_ARROW => T_DOUBLE_ARROW ];
669+ if (defined ('T_FN_ARROW ' ) === true ) {
670+ // PHPCS 3.5.3+.
671+ $ bodyTokens = [T_FN_ARROW => T_FN_ARROW ];
672+ }
673+ }
674+
675+ $ end = $ phpcsFile ->findNext (($ bodyTokens + [T_SEMICOLON ]), $ parenthesisCloser );
676+ $ hasBody = isset ($ bodyTokens [$ tokens [$ end ]['code ' ]]);
630677 }
631678
632679 if ($ returnType !== '' && $ nullableReturnType === true ) {
@@ -923,11 +970,13 @@ public static function getClassProperties(File $phpcsFile, $stackPtr)
923970 * - New by reference was not recognized as a reference.
924971 * - References to class properties with `self::`, `parent::`, `static::`,
925972 * `namespace\ClassName::`, `classname::` were not recognized as references.
973+ * - PHPCS 3.5.3: Added support for PHP 7.4 T_FN arrow functions returning by reference.
926974 *
927975 * @see \PHP_CodeSniffer\Files\File::isReference() Original source.
928976 * @see \PHPCSUtils\Utils\Operators::isReference() PHPCSUtils native improved version.
929977 *
930978 * @since 1.0.0
979+ * @since 1.0.0-alpha2 Added BC support for PHP 7.4 arrow functions.
931980 *
932981 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
933982 * @param int $stackPtr The position of the T_BITWISE_AND token.
@@ -945,7 +994,9 @@ public static function isReference(File $phpcsFile, $stackPtr)
945994
946995 $ tokenBefore = $ phpcsFile ->findPrevious (Tokens::$ emptyTokens , ($ stackPtr - 1 ), null , true );
947996
948- if ($ tokens [$ tokenBefore ]['code ' ] === T_FUNCTION ) {
997+ if ($ tokens [$ tokenBefore ]['code ' ] === T_FUNCTION
998+ || FunctionDeclarations::isArrowFunction ($ phpcsFile , $ tokenBefore ) === true
999+ ) {
9491000 // Function returns a reference.
9501001 return true ;
9511002 }
@@ -1116,10 +1167,12 @@ public static function getTokensAsString(File $phpcsFile, $start, $length, $orig
11161167 * Changelog for the PHPCS native function:
11171168 * - Introduced in PHPCS 2.1.0.
11181169 * - PHPCS 2.6.2: New optional `$ignore` parameter to selectively ignore stop points.
1170+ * - PHPCS 3.5.5: Added support for PHP 7.4 T_FN arrow functions.
11191171 *
11201172 * @see \PHP_CodeSniffer\Files\File::findStartOfStatement() Original source.
11211173 *
11221174 * @since 1.0.0
1175+ * @since 1.0.0-alpha2 Added BC support for PHP 7.4 arrow functions.
11231176 *
11241177 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
11251178 * @param int $start The position to start searching from in the token stack.
@@ -1158,6 +1211,7 @@ public static function findStartOfStatement(File $phpcsFile, $start, $ignore = n
11581211
11591212 if (isset ($ tokens [$ i ]['scope_opener ' ]) === true
11601213 && $ i === $ tokens [$ i ]['scope_closer ' ]
1214+ && $ tokens [$ i ]['code ' ] !== T_CLOSE_PARENTHESIS
11611215 ) {
11621216 // Found the end of the previous scope block.
11631217 return $ lastNotEmpty ;
@@ -1193,10 +1247,13 @@ public static function findStartOfStatement(File $phpcsFile, $start, $ignore = n
11931247 * - PHPCS 2.7.1: Improved handling of short arrays, PHPCS #1203.
11941248 * - PHPCS 3.3.0: Bug fix: end of statement detection when passed a scope opener, PHPCS #1863.
11951249 * - PHPCS 3.5.0: Improved handling of group use statements.
1250+ * - PHPCS 3.5.3: Added support for PHP 7.4 T_FN arrow functions.
1251+ * - PHPCS 3.5.4: Improved support for PHP 7.4 T_FN arrow functions.
11961252 *
11971253 * @see \PHP_CodeSniffer\Files\File::findEndOfStatement() Original source.
11981254 *
11991255 * @since 1.0.0
1256+ * @since 1.0.0-alpha2 Added BC support for PHP 7.4 arrow functions.
12001257 *
12011258 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
12021259 * @param int $start The position to start searching from in the token stack.
@@ -1251,6 +1308,12 @@ public static function findEndOfStatement(File $phpcsFile, $start, $ignore = nul
12511308 && ($ i === $ tokens [$ i ]['scope_opener ' ]
12521309 || $ i === $ tokens [$ i ]['scope_condition ' ])
12531310 ) {
1311+ if ($ tokens [$ i ]['type ' ] === 'T_FN ' ) {
1312+ // Minus 1 as the closer can be shared.
1313+ $ i = ($ tokens [$ i ]['scope_closer ' ] - 1 );
1314+ continue ;
1315+ }
1316+
12541317 if ($ i === $ start && isset (Tokens::$ scopeOpeners [$ tokens [$ i ]['code ' ]]) === true ) {
12551318 return $ tokens [$ i ]['scope_closer ' ];
12561319 }
@@ -1269,6 +1332,19 @@ public static function findEndOfStatement(File $phpcsFile, $start, $ignore = nul
12691332 if ($ end !== false ) {
12701333 $ i = $ end ;
12711334 }
1335+ } elseif ($ tokens [$ i ]['code ' ] === T_STRING || $ tokens [$ i ]['type ' ] === 'T_FN ' ) {
1336+ // Potentially a PHP 7.4 arrow function in combination with PHP < 7.4 or PHPCS < 3.5.3/3.5.4.
1337+ $ arrowFunctionOpenClose = FunctionDeclarations::getArrowFunctionOpenClose ($ phpcsFile , $ i );
1338+ if ($ arrowFunctionOpenClose !== []
1339+ && $ arrowFunctionOpenClose ['scope_closer ' ] !== false
1340+ ) {
1341+ if ($ i === $ start ) {
1342+ return $ arrowFunctionOpenClose ['scope_closer ' ];
1343+ }
1344+
1345+ // Minus 1 as the closer can be shared.
1346+ $ i = ($ arrowFunctionOpenClose ['scope_closer ' ] - 1 );
1347+ }
12721348 }
12731349
12741350 if (isset (Tokens::$ emptyTokens [$ tokens [$ i ]['code ' ]]) === false ) {
0 commit comments