Skip to content

Commit 70e8952

Browse files
committed
Fix NullToStrictStringFuncCallArgRector with probabilistic check for plural array passed to function where array allowed
1 parent 3c75872 commit 70e8952

3 files changed

Lines changed: 47 additions & 6 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector\Fixture;
4+
5+
final class SkipPregReplace
6+
{
7+
public static function run(&$items)
8+
{
9+
$pattern = "/(\d+')/";
10+
$replacement = '$1';
11+
$items = preg_replace($pattern, $replacement, $items);
12+
}
13+
}

rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PhpParser\Node\Expr\FuncCall;
1212
use PhpParser\Node\Expr\MethodCall;
1313
use PhpParser\Node\Expr\Ternary;
14+
use PhpParser\Node\Expr\Variable;
1415
use PhpParser\Node\Scalar\Int_;
1516
use PhpParser\Node\Scalar\InterpolatedString;
1617
use PhpParser\Node\Scalar\String_;
@@ -62,9 +63,9 @@ public function convertIfNull(
6263
return null;
6364
}
6465

65-
$parameter = $parametersAcceptor->getParameters()[$position] ?? null;
66-
if ($parameter instanceof ExtendedNativeParameterReflection && $parameter->getType() instanceof UnionType) {
67-
$parameterType = $parameter->getType();
66+
$parameterReflection = $parametersAcceptor->getParameters()[$position] ?? null;
67+
if ($parameterReflection instanceof ExtendedNativeParameterReflection && $parameterReflection->getType() instanceof UnionType) {
68+
$parameterType = $parameterReflection->getType();
6869
if (! $this->isValidUnionType($parameterType)) {
6970
return null;
7071
}
@@ -109,6 +110,10 @@ private function shouldSkipValue(Expr $expr, Scope $scope, bool $isTrait, string
109110
return true;
110111
}
111112

113+
if ($this->isPossibleArrayVariableName($type, $nativeType, $expr)) {
114+
return true;
115+
}
116+
112117
if ($this->shouldSkipType($type)) {
113118
return true;
114119
}
@@ -193,4 +198,27 @@ private function isAnErrorType(Expr $expr, Type $type, Scope $scope): bool
193198
&& ! $type->isExplicitMixed()
194199
&& $type->getSubtractedType() instanceof NullType;
195200
}
201+
202+
/**
203+
* @see https://github.com/rectorphp/rector/issues/9447 for context
204+
*/
205+
private function isPossibleArrayVariableName(Type $passedType, Type $reflectionParamType, Expr $expr): bool
206+
{
207+
// could mixed, resp. array, no need to (string) cast array
208+
if (! $passedType instanceof MixedType) {
209+
return false;
210+
}
211+
212+
if (! $reflectionParamType->isArray()->maybe()) {
213+
return false;
214+
}
215+
216+
if ($expr instanceof Variable && is_string($expr->name)) {
217+
$variableName = $expr->name;
218+
// most likely plural variable
219+
return strlen($variableName) > 3 && str_ends_with($variableName, 's');
220+
}
221+
222+
return false;
223+
}
196224
}

rules/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function refactor(Node $node): ?Node
8787
}
8888

8989
$args = $node->getArgs();
90-
$positions = $this->resolvePositions($node, $args, $scope);
90+
$positions = $this->resolveStringPositions($node, $args, $scope);
9191

9292
if ($positions === []) {
9393
return null;
@@ -133,9 +133,9 @@ public function provideMinPhpVersion(): int
133133

134134
/**
135135
* @param Arg[] $args
136-
* @return int[]|string[]
136+
* @return int[]
137137
*/
138-
private function resolvePositions(FuncCall $funcCall, array $args, Scope $scope): array
138+
private function resolveStringPositions(FuncCall $funcCall, array $args, Scope $scope): array
139139
{
140140
$positions = [];
141141

0 commit comments

Comments
 (0)