Skip to content

Commit 8fedf8c

Browse files
committed
The PHP 7.4 T_FN token has been made available for older versions (ref #2523)
1 parent 6a4abf4 commit 8fedf8c

File tree

5 files changed

+127
-1
lines changed

5 files changed

+127
-1
lines changed

package.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
2626
</stability>
2727
<license uri="https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt">BSD 3-Clause License</license>
2828
<notes>
29+
- The PHP 7.4 T_FN token has been made available for older versions
30+
-- T_FN represents the fn string used for arrow functions
31+
-- The token is associated with the opening and closing parenthesis of the statement
2932
- Generic.CodeAnalysis.EmptyPhpStatement now reports unnecessary semicolons after control structure closing braces
3033
-- Thanks to Vincent Langlet for the patch
3134
- Fixed bug #2638 : Squiz.CSS.DuplicateClassDefinitionSniff sees comments as part of the class name
@@ -89,6 +92,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
8992
<dir name="Tokenizer">
9093
<file baseinstalldir="" name="AnonClassParenthesisOwnerTest.inc" role="test" />
9194
<file baseinstalldir="" name="AnonClassParenthesisOwnerTest.php" role="test" />
95+
<file baseinstalldir="" name="BackfillFnTokenTest.inc" role="test" />
96+
<file baseinstalldir="" name="BackfillFnTokenTest.php" role="test" />
9297
</dir>
9398
<file baseinstalldir="" name="AbstractMethodUnitTest.php" role="test" />
9499
<file baseinstalldir="" name="AllTests.php" role="test" />
@@ -1917,6 +1922,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
19171922
<install as="CodeSniffer/Core/Filters/Filter/AcceptTest.xml" name="tests/Core/Filters/Filter/AcceptTest.xml" />
19181923
<install as="CodeSniffer/Core/Tokenizer/AnonClassParenthesisOwnerTest.php" name="tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.php" />
19191924
<install as="CodeSniffer/Core/Tokenizer/AnonClassParenthesisOwnerTest.inc" name="tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.inc" />
1925+
<install as="CodeSniffer/Core/Tokenizer/BackfillFnTokenTest.php" name="tests/Core/Tokenizer/BackfillFnTokenTest.php" />
1926+
<install as="CodeSniffer/Core/Tokenizer/BackfillFnTokenTest.inc" name="tests/Core/Tokenizer/BackfillFnTokenTest.inc" />
19201927
<install as="CodeSniffer/Standards/AllSniffs.php" name="tests/Standards/AllSniffs.php" />
19211928
<install as="CodeSniffer/Standards/AbstractSniffUnitTest.php" name="tests/Standards/AbstractSniffUnitTest.php" />
19221929
</filelist>
@@ -1956,6 +1963,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
19561963
<install as="CodeSniffer/Core/Filters/Filter/AcceptTest.xml" name="tests/Core/Filters/Filter/AcceptTest.xml" />
19571964
<install as="CodeSniffer/Core/Tokenizer/AnonClassParenthesisOwnerTest.php" name="tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.php" />
19581965
<install as="CodeSniffer/Core/Tokenizer/AnonClassParenthesisOwnerTest.inc" name="tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.inc" />
1966+
<install as="CodeSniffer/Core/Tokenizer/BackfillFnTokenTest.php" name="tests/Core/Tokenizer/BackfillFnTokenTest.php" />
1967+
<install as="CodeSniffer/Core/Tokenizer/BackfillFnTokenTest.inc" name="tests/Core/Tokenizer/BackfillFnTokenTest.inc" />
19591968
<install as="CodeSniffer/Standards/AllSniffs.php" name="tests/Standards/AllSniffs.php" />
19601969
<install as="CodeSniffer/Standards/AbstractSniffUnitTest.php" name="tests/Standards/AbstractSniffUnitTest.php" />
19611970
<ignore name="bin/phpcs.bat" />

src/Tokenizers/PHP.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1045,7 +1045,7 @@ protected function tokenize($string)
10451045
}//end if
10461046

10471047
/*
1048-
Tokens after a double colon may be look like scope openers,
1048+
Tokens after a double colon may look like scope openers,
10491049
such as when writing code like Foo::NAMESPACE, but they are
10501050
only ever variables or strings.
10511051
*/
@@ -1606,6 +1606,34 @@ protected function processAdditional()
16061606
}//end if
16071607

16081608
continue;
1609+
} else if ($this->tokens[$i]['code'] === T_STRING
1610+
&& strtolower($this->tokens[$i]['content']) === 'fn'
1611+
) {
1612+
// Possible arrow function.
1613+
for ($x = ($i + 1); $i < $numTokens; $x++) {
1614+
if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
1615+
// Non-whitespace content.
1616+
break;
1617+
}
1618+
}
1619+
1620+
if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
1621+
if (PHP_CODESNIFFER_VERBOSITY > 1) {
1622+
$line = $this->tokens[$i]['line'];
1623+
echo "\t* token $i on line $line changed from T_STRING to T_FN".PHP_EOL;
1624+
}
1625+
1626+
$this->tokens[$i]['code'] = T_FN;
1627+
$this->tokens[$i]['type'] = 'T_FN';
1628+
$this->tokens[$i]['parenthesis_owner'] = $i;
1629+
$this->tokens[$i]['parenthesis_opener'] = $x;
1630+
$this->tokens[$i]['parenthesis_closer'] = $this->tokens[$x]['parenthesis_closer'];
1631+
1632+
$opener = $this->tokens[$i]['parenthesis_opener'];
1633+
$closer = $this->tokens[$i]['parenthesis_closer'];
1634+
$this->tokens[$opener]['parenthesis_owner'] = $i;
1635+
$this->tokens[$closer]['parenthesis_owner'] = $i;
1636+
}
16091637
} else if ($this->tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET) {
16101638
if (isset($this->tokens[$i]['bracket_closer']) === false) {
16111639
continue;

src/Util/Tokens.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@
119119
define('T_BAD_CHARACTER', 'PHPCS_T_BAD_CHARACTER');
120120
}
121121

122+
if (defined('T_FN') === false) {
123+
define('T_FN', 'PHPCS_T_FN');
124+
}
125+
122126
// Tokens used for parsing doc blocks.
123127
define('T_DOC_COMMENT_STAR', 'PHPCS_T_DOC_COMMENT_STAR');
124128
define('T_DOC_COMMENT_WHITESPACE', 'PHPCS_T_DOC_COMMENT_WHITESPACE');
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/* testStandard */
4+
$fn1 = fn($x) => $x + $y;
5+
6+
/* testMixedCase */
7+
$fn1 = Fn($x) => $x + $y;
8+
9+
/* testWhitespace */
10+
$fn1 = fn ($x) => $x + $y;
11+
12+
/* testComment */
13+
$fn1 = fn /* comment here */ ($x) => $x + $y;
14+
15+
/* testFunctionName */
16+
function fn() {}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
/**
3+
* Tests the backfilling of the T_FN token to PHP < 7.4.
4+
*
5+
* @author Greg Sherwood <gsherwood@squiz.net>
6+
* @copyright 2019 Squiz Pty Ltd (ABN 77 084 670 600)
7+
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8+
*/
9+
10+
namespace PHP_CodeSniffer\Tests\Core\Tokenizer;
11+
12+
use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
13+
14+
class BackfillFnTokenTest extends AbstractMethodUnitTest
15+
{
16+
17+
18+
/**
19+
* Data provider.
20+
*
21+
* @see testBackill()
22+
*
23+
* @return array
24+
*/
25+
public function dataBackfill()
26+
{
27+
return [
28+
['/* testStandard */'],
29+
['/* testMixedCase */'],
30+
['/* testWhitespace */'],
31+
['/* testComment */'],
32+
['/* testFunctionName */'],
33+
];
34+
35+
}//end dataAnonClassNoParentheses()
36+
37+
38+
/**
39+
* Test that anonymous class tokens without parenthesis do not get assigned a parenthesis owner.
40+
*
41+
* @param string $testMarker The comment which prefaces the target token in the test file.
42+
*
43+
* @dataProvider dataBackfill
44+
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
45+
*
46+
* @return void
47+
*/
48+
public function testBackfill($testMarker)
49+
{
50+
$tokens = self::$phpcsFile->getTokens();
51+
52+
$token = $this->getTargetToken($testMarker, T_FN);
53+
$this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$token]), 'Parenthesis owner is not set');
54+
$this->assertTrue(array_key_exists('parenthesis_opener', $tokens[$token]), 'Parenthesis opener is not set');
55+
$this->assertTrue(array_key_exists('parenthesis_closer', $tokens[$token]), 'Parenthesis closer is not set');
56+
$this->assertSame($tokens[$token]['parenthesis_owner'], $token, 'Parenthesis owner is not the T_FN token');
57+
58+
$opener = $tokens[$token]['parenthesis_opener'];
59+
$this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$opener]), 'Opening parenthesis owner is not set');
60+
$this->assertSame($tokens[$opener]['parenthesis_owner'], $token, 'Opening parenthesis owner is not the T_FN token');
61+
62+
$closer = $tokens[$token]['parenthesis_closer'];
63+
$this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$closer]), 'Closing parenthesis owner is not set');
64+
$this->assertSame($tokens[$closer]['parenthesis_owner'], $token, 'Closing parenthesis owner is not the T_FN token');
65+
66+
}//end testAnonClassNoParentheses()
67+
68+
69+
}//end class

0 commit comments

Comments
 (0)