Skip to content

Commit 00af280

Browse files
committed
Fix false positive non-existing-offset after array_search()
1 parent 5b2b69f commit 00af280

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed

Diff for: src/Analyser/TypeSpecifier.php

+25-1
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ public function specifyTypesInCondition(
664664
if (!$scope instanceof MutatingScope) {
665665
throw new ShouldNotHappenException();
666666
}
667+
667668
if ($context->null()) {
668669
$specifiedTypes = $this->specifyTypesInCondition($scope->exitFirstLevelStatements(), $expr->expr, $context)->setRootExpr($expr);
669670

@@ -718,7 +719,30 @@ public function specifyTypesInCondition(
718719
return $specifiedTypes;
719720
}
720721

721-
return $this->specifyTypesInCondition($scope->exitFirstLevelStatements(), $expr->var, $context)->setRootExpr($expr);
722+
$specifiedTypes = $this->specifyTypesInCondition($scope->exitFirstLevelStatements(), $expr->var, $context)->setRootExpr($expr);
723+
724+
if ($context->true()) {
725+
// infer $arr[$key] after $key = array_search($needle, $arr)
726+
if (
727+
$expr->expr instanceof FuncCall
728+
&& $expr->expr->name instanceof Name
729+
&& in_array($expr->expr->name->toLowerString(), ['array_search'], true)
730+
&& count($expr->expr->getArgs()) >= 2
731+
) {
732+
$arrayArg = $expr->expr->getArgs()[1]->value;
733+
$arrayType = $scope->getType($arrayArg);
734+
735+
if ($arrayType->isArray()->yes()) {
736+
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
737+
$iterableValueType = $arrayType->getIterableValueType();
738+
739+
return $specifiedTypes->unionWith(
740+
$this->create($dimFetch, $iterableValueType, TypeSpecifierContext::createTrue(), $scope),
741+
);
742+
}
743+
}
744+
}
745+
return $specifiedTypes;
722746
} elseif (
723747
$expr instanceof Expr\Isset_
724748
&& count($expr->vars) > 0

Diff for: tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

+12
Original file line numberDiff line numberDiff line change
@@ -812,4 +812,16 @@ public function testArrayDimFetchAfterCount(): void
812812
]);
813813
}
814814

815+
public function testArrayDimFetchAfterArraySearch(): void
816+
{
817+
$this->reportPossiblyNonexistentGeneralArrayOffset = true;
818+
819+
$this->analyse([__DIR__ . '/data/array-dim-after-array-search.php'], [
820+
[
821+
'Offset int|string might not exist on array.',
822+
20,
823+
],
824+
]);
825+
}
826+
815827
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace ArrayDimAfterArraySeach;
6+
7+
class HelloWorld
8+
{
9+
public function doFoo(array $arr, string $needle): string
10+
{
11+
if (($key = array_search($needle, $arr, true)) !== false) {
12+
echo $arr[$key];
13+
}
14+
}
15+
16+
public function doBar(array $arr, string $needle): string
17+
{
18+
$key = array_search($needle, $arr, true);
19+
if ($key !== false) {
20+
echo $arr[$key];
21+
}
22+
}
23+
24+
public function doFooBar(array $arr, string $needle): string
25+
{
26+
if (($key = array_search($needle, $arr, false)) !== false) {
27+
echo $arr[$key];
28+
}
29+
}
30+
31+
public function doBaz(array $arr, string $needle): string
32+
{
33+
if (($key = array_search($needle, $arr)) !== false) {
34+
echo $arr[$key];
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)