Skip to content

Commit 814df6b

Browse files
committed
Ensure compatibility with PHP 7.4 arrow functions
1 parent 1899948 commit 814df6b

36 files changed

+933
-41
lines changed

.composer-require-checker.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"null", "true", "false",
1717
"static", "self", "parent",
1818
"array", "string", "int", "float", "bool", "iterable", "callable", "void",
19-
"T_COALESCE_EQUAL"
19+
"T_COALESCE_EQUAL", "T_FN"
2020
],
2121
"php-core-extensions" : [
2222
"dom", "mbstring", "Phar",

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
include:
2727
-
2828
stage: Static Code Analysis
29-
php: 7.2
29+
php: 7.3
3030
env: COMPOSER_FLAGS="--prefer-stable"
3131
install:
3232
- travis_retry ./dev-tools/install.sh

phpstan.neon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ parameters:
1111
ignoreErrors:
1212
- '/^.+::__construct\(\) does not call parent constructor from SplFileInfo\.$/'
1313
- '/^Return typehint of method PhpCsFixer\\Tests\\Test\\.+::createIsIdenticalStringConstraint\(\) has invalid type PHPUnit_Framework_Constraint_IsIdentical\.$/'
14+
## cannot analyse out of PHP 7.4
15+
- '/^Constant T_FN not found\.$/'

src/Fixer/FunctionNotation/FunctionDeclarationFixer.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
1919
use PhpCsFixer\FixerDefinition\CodeSample;
2020
use PhpCsFixer\FixerDefinition\FixerDefinition;
21+
use PhpCsFixer\FixerDefinition\VersionSpecification;
22+
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
2123
use PhpCsFixer\Tokenizer\CT;
2224
use PhpCsFixer\Tokenizer\Tokens;
2325
use PhpCsFixer\Tokenizer\TokensAnalyzer;
@@ -48,6 +50,10 @@ final class FunctionDeclarationFixer extends AbstractFixer implements Configurat
4850
*/
4951
public function isCandidate(Tokens $tokens)
5052
{
53+
if (\PHP_VERSION_ID >= 70400 && $tokens->isTokenKindFound(T_FN)) {
54+
return true;
55+
}
56+
5157
return $tokens->isTokenKindFound(T_FUNCTION);
5258
}
5359

@@ -82,6 +88,13 @@ function foo ($bar, $baz)
8288
',
8389
['closure_function_spacing' => self::SPACING_NONE]
8490
),
91+
new VersionSpecificCodeSample(
92+
'<?php
93+
$f = fn () => null;
94+
',
95+
new VersionSpecification(70400),
96+
['closure_function_spacing' => self::SPACING_NONE]
97+
),
8598
]
8699
);
87100
}
@@ -96,7 +109,10 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
96109
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
97110
$token = $tokens[$index];
98111

99-
if (!$token->isGivenKind(T_FUNCTION)) {
112+
if (
113+
!$token->isGivenKind(T_FUNCTION)
114+
&& (\PHP_VERSION_ID < 70400 || !$token->isGivenKind(T_FN))
115+
) {
100116
continue;
101117
}
102118

@@ -106,13 +122,14 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
106122
}
107123

108124
$endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startParenthesisIndex);
109-
$startBraceIndex = $tokens->getNextTokenOfKind($endParenthesisIndex, [';', '{']);
125+
$startBraceIndex = $tokens->getNextTokenOfKind($endParenthesisIndex, [';', '{', [T_DOUBLE_ARROW]]);
110126

111-
// fix single-line whitespace before {
127+
// fix single-line whitespace before { or =>
112128
// eg: `function foo(){}` => `function foo() {}`
113129
// eg: `function foo() {}` => `function foo() {}`
130+
// eg: `fn() =>` => `fn() =>`
114131
if (
115-
$tokens[$startBraceIndex]->equals('{') &&
132+
$tokens[$startBraceIndex]->equalsAny(['{', [T_DOUBLE_ARROW]]) &&
116133
(
117134
!$tokens[$startBraceIndex - 1]->isWhitespace() ||
118135
$tokens[$startBraceIndex - 1]->isWhitespace($this->singleLineWhitespaceOptions)

src/Fixer/FunctionNotation/FunctionTypehintSpaceFixer.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ public function getDefinition()
4444
*/
4545
public function isCandidate(Tokens $tokens)
4646
{
47+
if (\PHP_VERSION_ID >= 70400 && $tokens->isTokenKindFound(T_FN)) {
48+
return true;
49+
}
50+
4751
return $tokens->isTokenKindFound(T_FUNCTION);
4852
}
4953

@@ -57,7 +61,10 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
5761
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
5862
$token = $tokens[$index];
5963

60-
if (!$token->isGivenKind(T_FUNCTION)) {
64+
if (
65+
!$token->isGivenKind(T_FUNCTION)
66+
&& (\PHP_VERSION_ID < 70400 || !$token->isGivenKind(T_FN))
67+
) {
6168
continue;
6269
}
6370

src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ public function getPriority()
139139
*/
140140
protected function applyFix(\SplFileInfo $file, Tokens $tokens)
141141
{
142+
$expectedTokens = [T_LIST, T_FUNCTION];
143+
if (\PHP_VERSION_ID >= 70400) {
144+
$expectedTokens[] = T_FN;
145+
}
146+
142147
for ($index = $tokens->count() - 1; $index > 0; --$index) {
143148
$token = $tokens[$index];
144149

@@ -149,7 +154,7 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
149154
$meaningfulTokenBeforeParenthesis = $tokens[$tokens->getPrevMeaningfulToken($index)];
150155
if (
151156
$meaningfulTokenBeforeParenthesis->isKeyword()
152-
&& !$meaningfulTokenBeforeParenthesis->isGivenKind([T_LIST, T_FUNCTION])
157+
&& !$meaningfulTokenBeforeParenthesis->isGivenKind($expectedTokens)
153158
) {
154159
continue;
155160
}

src/Fixer/FunctionNotation/NoUnreachableDefaultArgumentValueFixer.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ function example($foo = "two words", $bar) {}
4949
*/
5050
public function isCandidate(Tokens $tokens)
5151
{
52+
if (\PHP_VERSION_ID >= 70400 && $tokens->isTokenKindFound(T_FN)) {
53+
return true;
54+
}
55+
5256
return $tokens->isTokenKindFound(T_FUNCTION);
5357
}
5458

@@ -66,7 +70,10 @@ public function isRisky()
6670
protected function applyFix(\SplFileInfo $file, Tokens $tokens)
6771
{
6872
for ($i = 0, $l = $tokens->count(); $i < $l; ++$i) {
69-
if (!$tokens[$i]->isGivenKind(T_FUNCTION)) {
73+
if (
74+
!$tokens[$i]->isGivenKind(T_FUNCTION)
75+
&& (\PHP_VERSION_ID < 70400 || !$tokens[$i]->isGivenKind(T_FN))
76+
) {
7077
continue;
7178
}
7279

src/Fixer/FunctionNotation/PhpdocToReturnTypeFixer.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ function my_foo()
121121
*/
122122
public function isCandidate(Tokens $tokens)
123123
{
124+
if (\PHP_VERSION_ID >= 70400 && $tokens->isTokenKindFound(T_FN)) {
125+
return true;
126+
}
127+
124128
return \PHP_VERSION_ID >= 70000 && $tokens->isTokenKindFound(T_FUNCTION);
125129
}
126130

@@ -161,7 +165,10 @@ protected function createConfigurationDefinition()
161165
protected function applyFix(\SplFileInfo $file, Tokens $tokens)
162166
{
163167
for ($index = $tokens->count() - 1; 0 < $index; --$index) {
164-
if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
168+
if (
169+
!$tokens[$index]->isGivenKind(T_FUNCTION)
170+
&& (\PHP_VERSION_ID < 70400 || !$tokens[$index]->isGivenKind(T_FN))
171+
) {
165172
continue;
166173
}
167174

src/Fixer/FunctionNotation/StaticLambdaFixer.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ public function getDefinition()
4343
*/
4444
public function isCandidate(Tokens $tokens)
4545
{
46+
if (\PHP_VERSION_ID >= 70400 && $tokens->isTokenKindFound(T_FN)) {
47+
return true;
48+
}
49+
4650
return $tokens->isTokenKindFound(T_FUNCTION);
4751
}
4852

@@ -61,8 +65,13 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
6165
{
6266
$analyzer = new TokensAnalyzer($tokens);
6367

68+
$expectedFunctionKinds = [T_FUNCTION];
69+
if (\PHP_VERSION_ID >= 70400) {
70+
$expectedFunctionKinds[] = T_FN;
71+
}
72+
6473
for ($index = $tokens->count() - 4; $index > 0; --$index) {
65-
if (!$tokens[$index]->isGivenKind(T_FUNCTION) || !$analyzer->isLambda($index)) {
74+
if (!$tokens[$index]->isGivenKind($expectedFunctionKinds) || !$analyzer->isLambda($index)) {
6675
continue;
6776
}
6877

@@ -71,11 +80,18 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
7180
continue; // lambda is already 'static'
7281
}
7382

83+
$argumentsStartIndex = $tokens->getNextTokenOfKind($index, ['(']);
84+
$argumentsEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $argumentsStartIndex);
85+
7486
// figure out where the lambda starts ...
75-
$lambdaOpenIndex = $tokens->getNextTokenOfKind($index, ['{']);
87+
$lambdaOpenIndex = $tokens->getNextTokenOfKind($argumentsEndIndex, ['{', [T_DOUBLE_ARROW]]);
7688

7789
// ... and where it ends
78-
$lambdaEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $lambdaOpenIndex);
90+
if ($tokens[$lambdaOpenIndex]->isGivenKind(T_DOUBLE_ARROW)) {
91+
$lambdaEndIndex = $tokens->getNextTokenOfKind($lambdaOpenIndex, [';']);
92+
} else {
93+
$lambdaEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $lambdaOpenIndex);
94+
}
7995

8096
if ($this->hasPossibleReferenceToThis($tokens, $lambdaOpenIndex, $lambdaEndIndex)) {
8197
continue;

src/Tokenizer/Analyzer/FunctionsAnalyzer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public function getFunctionReturnType(Tokens $tokens, $methodIndex)
8181
$type = '';
8282
$typeStartIndex = $tokens->getNextMeaningfulToken($typeColonIndex);
8383
$typeEndIndex = $typeStartIndex;
84-
$functionBodyStart = $tokens->getNextTokenOfKind($typeColonIndex, ['{', ';']);
84+
$functionBodyStart = $tokens->getNextTokenOfKind($typeColonIndex, ['{', ';', [T_DOUBLE_ARROW]]);
8585
for ($i = $typeStartIndex; $i < $functionBodyStart; ++$i) {
8686
if ($tokens[$i]->isWhitespace() || $tokens[$i]->isComment()) {
8787
continue;

0 commit comments

Comments
 (0)