Skip to content

Commit 79aed74

Browse files
authored
Resolve PHPStan issue with deprecated parameter order in PHP 8.1 and 8.3
1 parent e7d6c3a commit 79aed74

10 files changed

+527
-10
lines changed

src/Php/PhpVersion.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ public function deprecatesRequiredParameterAfterOptional(): bool
5656
return $this->versionId >= 80000;
5757
}
5858

59+
public function deprecatesRequiredParameterAfterOptionalNullableAndDefaultNull(): bool
60+
{
61+
return $this->versionId >= 80100;
62+
}
63+
64+
public function deprecatesRequiredParameterAfterOptionalUnionOrMixed(): bool
65+
{
66+
return $this->versionId >= 80300;
67+
}
68+
5969
public function supportsLessOverridenParametersWithVariadic(): bool
6070
{
6171
return $this->versionId >= 80000;

src/Rules/FunctionDefinitionCheck.php

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
use PhpParser\Node\Expr\ConstFetch;
77
use PhpParser\Node\Expr\Variable;
88
use PhpParser\Node\FunctionLike;
9+
use PhpParser\Node\Identifier;
10+
use PhpParser\Node\IntersectionType;
11+
use PhpParser\Node\Name;
12+
use PhpParser\Node\NullableType;
913
use PhpParser\Node\Param;
1014
use PhpParser\Node\Stmt\ClassMethod;
1115
use PhpParser\Node\Stmt\Function_;
@@ -29,10 +33,12 @@
2933
use PHPStan\Type\Type;
3034
use PHPStan\Type\TypeTraverser;
3135
use PHPStan\Type\VerbosityLevel;
36+
use function array_filter;
3237
use function array_keys;
3338
use function array_map;
3439
use function array_merge;
3540
use function count;
41+
use function in_array;
3642
use function is_string;
3743
use function sprintf;
3844

@@ -366,6 +372,7 @@ private function checkRequiredParameterAfterOptional(array $parameterNodes): arr
366372
/** @var string|null $optionalParameter */
367373
$optionalParameter = null;
368374
$errors = [];
375+
$targetPhpVersion = null;
369376
foreach ($parameterNodes as $parameterNode) {
370377
if (!$parameterNode->var instanceof Variable) {
371378
throw new ShouldNotHappenException();
@@ -375,7 +382,16 @@ private function checkRequiredParameterAfterOptional(array $parameterNodes): arr
375382
}
376383
$parameterName = $parameterNode->var->name;
377384
if ($optionalParameter !== null && $parameterNode->default === null && !$parameterNode->variadic) {
378-
$errors[] = RuleErrorBuilder::message(sprintf('Deprecated in PHP 8.0: Required parameter $%s follows optional parameter $%s.', $parameterName, $optionalParameter))->line($parameterNode->getStartLine())->build();
385+
$errors[] = RuleErrorBuilder::message(
386+
sprintf(
387+
'Deprecated in PHP %s: Required parameter $%s follows optional parameter $%s.',
388+
$targetPhpVersion ??= '8.0',
389+
$parameterName,
390+
$optionalParameter,
391+
),
392+
)->line($parameterNode->getStartLine())->build();
393+
394+
$targetPhpVersion = null;
379395
continue;
380396
}
381397
if ($parameterNode->default === null) {
@@ -394,7 +410,35 @@ private function checkRequiredParameterAfterOptional(array $parameterNodes): arr
394410

395411
$constantName = $defaultValue->name->toLowerString();
396412
if ($constantName === 'null') {
397-
continue;
413+
if (!$this->phpVersion->deprecatesRequiredParameterAfterOptionalNullableAndDefaultNull()) {
414+
continue;
415+
}
416+
417+
$parameterNodeType = $parameterNode->type;
418+
419+
if ($parameterNodeType instanceof NullableType) {
420+
$targetPhpVersion = '8.1';
421+
}
422+
423+
if ($this->phpVersion->deprecatesRequiredParameterAfterOptionalUnionOrMixed()) {
424+
$types = [];
425+
426+
if ($parameterNodeType instanceof UnionType) {
427+
$types = $parameterNodeType->types;
428+
} elseif ($parameterNodeType instanceof Identifier) {
429+
$types = [$parameterNodeType];
430+
}
431+
432+
$nullOrMixed = array_filter($types, static fn (Identifier|Name|IntersectionType $type): bool => $type instanceof Identifier && (in_array($type->name, ['null', 'mixed'], true)));
433+
434+
if (0 < count($nullOrMixed)) {
435+
$targetPhpVersion = '8.3';
436+
}
437+
}
438+
439+
if ($targetPhpVersion === null) {
440+
continue;
441+
}
398442
}
399443

400444
$optionalParameter = $parameterName;

tests/PHPStan/Rules/Functions/ExistingClassesInArrowFunctionTypehintsRuleTest.php

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,16 @@ public function dataRequiredParameterAfterOptional(): array
9191
return [
9292
[
9393
70400,
94-
[],
94+
[
95+
[
96+
"Anonymous function uses native union types but they're supported only on PHP 8.0 and later.",
97+
17,
98+
],
99+
[
100+
"Anonymous function uses native union types but they're supported only on PHP 8.0 and later.",
101+
19,
102+
],
103+
],
95104
],
96105
[
97106
80000,
@@ -108,6 +117,92 @@ public function dataRequiredParameterAfterOptional(): array
108117
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
109118
11,
110119
],
120+
[
121+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
122+
13,
123+
],
124+
[
125+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
126+
17,
127+
],
128+
[
129+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
130+
21,
131+
],
132+
],
133+
],
134+
[
135+
80100,
136+
[
137+
[
138+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
139+
5,
140+
],
141+
[
142+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
143+
9,
144+
],
145+
[
146+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
147+
11,
148+
],
149+
[
150+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
151+
13,
152+
],
153+
[
154+
'Deprecated in PHP 8.1: Required parameter $bar follows optional parameter $foo.',
155+
15,
156+
],
157+
[
158+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
159+
17,
160+
],
161+
[
162+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
163+
21,
164+
],
165+
],
166+
],
167+
[
168+
80300,
169+
[
170+
[
171+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
172+
5,
173+
],
174+
[
175+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
176+
9,
177+
],
178+
[
179+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
180+
11,
181+
],
182+
[
183+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
184+
13,
185+
],
186+
[
187+
'Deprecated in PHP 8.1: Required parameter $bar follows optional parameter $foo.',
188+
15,
189+
],
190+
[
191+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
192+
17,
193+
],
194+
[
195+
'Deprecated in PHP 8.3: Required parameter $bar follows optional parameter $foo.',
196+
19,
197+
],
198+
[
199+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
200+
21,
201+
],
202+
[
203+
'Deprecated in PHP 8.3: Required parameter $bar follows optional parameter $foo.',
204+
23,
205+
],
111206
],
112207
],
113208
];

tests/PHPStan/Rules/Functions/ExistingClassesInClosureTypehintsRuleTest.php

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,16 @@ public function dataRequiredParameterAfterOptional(): array
135135
return [
136136
[
137137
70400,
138-
[],
138+
[
139+
[
140+
"Anonymous function uses native union types but they're supported only on PHP 8.0 and later.",
141+
29,
142+
],
143+
[
144+
"Anonymous function uses native union types but they're supported only on PHP 8.0 and later.",
145+
33,
146+
],
147+
],
139148
],
140149
[
141150
80000,
@@ -152,6 +161,92 @@ public function dataRequiredParameterAfterOptional(): array
152161
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
153162
17,
154163
],
164+
[
165+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
166+
21,
167+
],
168+
[
169+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
170+
29,
171+
],
172+
[
173+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
174+
37,
175+
],
176+
],
177+
],
178+
[
179+
80100,
180+
[
181+
[
182+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
183+
5,
184+
],
185+
[
186+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
187+
13,
188+
],
189+
[
190+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
191+
17,
192+
],
193+
[
194+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
195+
21,
196+
],
197+
[
198+
'Deprecated in PHP 8.1: Required parameter $bar follows optional parameter $foo.',
199+
25,
200+
],
201+
[
202+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
203+
29,
204+
],
205+
[
206+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
207+
37,
208+
],
209+
],
210+
],
211+
[
212+
80300,
213+
[
214+
[
215+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
216+
5,
217+
],
218+
[
219+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
220+
13,
221+
],
222+
[
223+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
224+
17,
225+
],
226+
[
227+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
228+
21,
229+
],
230+
[
231+
'Deprecated in PHP 8.1: Required parameter $bar follows optional parameter $foo.',
232+
25,
233+
],
234+
[
235+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
236+
29,
237+
],
238+
[
239+
'Deprecated in PHP 8.3: Required parameter $bar follows optional parameter $foo.',
240+
33,
241+
],
242+
[
243+
'Deprecated in PHP 8.0: Required parameter $bar follows optional parameter $foo.',
244+
37,
245+
],
246+
[
247+
'Deprecated in PHP 8.3: Required parameter $bar follows optional parameter $foo.',
248+
41,
249+
],
155250
],
156251
],
157252
];

0 commit comments

Comments
 (0)