Skip to content

Commit a413d3c

Browse files
phpstan-botclaude
andcommitted
De-duplicate ConditionalExpressionHolder creation for array dim fetch tracking
Extract createArrayDimFetchConditionalExpressionHolder() helper method and merge the separate array_search/array_find_key blocks into one, reducing three instances of the same ~12-line pattern to single-line calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f39a922 commit a413d3c

1 file changed

Lines changed: 57 additions & 87 deletions

File tree

src/Analyser/TypeSpecifier.php

Lines changed: 57 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -848,113 +848,62 @@ public function specifyTypesInCondition(
848848
$this->create($dimFetch, $arrayType->getIterableValueType(), TypeSpecifierContext::createTrue(), $scope),
849849
);
850850
} elseif ($expr->var instanceof Expr\Variable && is_string($expr->var->name)) {
851-
// The array might be empty here, so we cannot register
852-
// $arr[$key] unconditionally. Attach a conditional holder
853-
// that fires once the user narrows $key to non-null
854-
// (e.g. `if ($key !== null)`), giving the deep-write
855-
// path the same shape information `isset($arr[$key])`
856-
// would have provided.
857851
$keyType = $scope->getType($expr->expr);
858852
$nonNullKeyType = TypeCombinator::removeNull($keyType);
859-
if (!$nonNullKeyType instanceof NeverType && !$keyType->isNull()->yes()) {
860-
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
861-
$dimFetchString = $this->exprPrinter->printExpr($dimFetch);
862-
$keyExprString = $this->exprPrinter->printExpr($expr->var);
863-
864-
$holder = new ConditionalExpressionHolder(
865-
[$keyExprString => ExpressionTypeHolder::createYes($expr->var, $nonNullKeyType)],
866-
ExpressionTypeHolder::createYes($dimFetch, $arrayType->getIterableValueType()),
867-
);
868-
869-
$specifiedTypes = $specifiedTypes->unionWith(
870-
(new SpecifiedTypes([], []))->setNewConditionalExpressionHolders([
871-
$dimFetchString => [$holder->getKey() => $holder],
872-
]),
873-
);
874-
}
875-
}
876-
}
877-
}
878-
879-
// infer $arr[$key] after $key = array_search($needle, $arr)
880-
if (
881-
$expr->expr instanceof FuncCall
882-
&& $expr->expr->name instanceof Name
883-
&& $expr->expr->name->toLowerString() === 'array_search'
884-
&& count($expr->expr->getArgs()) >= 2
885-
) {
886-
$arrayArg = $expr->expr->getArgs()[1]->value;
887-
$arrayType = $scope->getType($arrayArg);
888-
889-
if ($arrayType->isArray()->yes()) {
890-
if ($context->true()) {
891-
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
892-
893-
$specifiedTypes = $specifiedTypes->unionWith(
894-
$this->create($dimFetch, $arrayType->getIterableValueType(), TypeSpecifierContext::createTrue(), $scope),
895-
);
896-
} elseif ($expr->var instanceof Expr\Variable && is_string($expr->var->name)) {
897-
$keyType = $scope->getType($expr->expr);
898-
$nonFalseKeyType = TypeCombinator::remove($keyType, new ConstantBooleanType(false));
899-
if (!$nonFalseKeyType instanceof NeverType && !$keyType->isFalse()->yes()) {
900-
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
901-
$dimFetchString = $this->exprPrinter->printExpr($dimFetch);
902-
$keyExprString = $this->exprPrinter->printExpr($expr->var);
903-
904-
$holder = new ConditionalExpressionHolder(
905-
[$keyExprString => ExpressionTypeHolder::createYes($expr->var, $nonFalseKeyType)],
906-
ExpressionTypeHolder::createYes($dimFetch, $arrayType->getIterableValueType()),
907-
);
908-
853+
if (!$nonNullKeyType instanceof NeverType) {
909854
$specifiedTypes = $specifiedTypes->unionWith(
910-
(new SpecifiedTypes([], []))->setNewConditionalExpressionHolders([
911-
$dimFetchString => [$holder->getKey() => $holder],
912-
]),
855+
$this->createArrayDimFetchConditionalExpressionHolder($expr->var, $arrayArg, $arrayType, $nonNullKeyType),
913856
);
914857
}
915858
}
916859
}
917860
}
918861

919-
// infer $arr[$key] after $key = array_find_key($arr, $callback)
862+
// infer $arr[$key] after $key = array_search($needle, $arr) or $key = array_find_key($arr, $callback)
920863
if (
921864
$expr->expr instanceof FuncCall
922865
&& $expr->expr->name instanceof Name
923-
&& $expr->expr->name->toLowerString() === 'array_find_key'
924866
&& count($expr->expr->getArgs()) >= 2
925867
) {
926-
$arrayArg = $expr->expr->getArgs()[0]->value;
927-
$arrayType = $scope->getType($arrayArg);
928-
929-
if ($arrayType->isArray()->yes()) {
930-
if ($context->true()) {
931-
$specifiedTypes = $specifiedTypes->unionWith(
932-
$this->create($arrayArg, new NonEmptyArrayType(), TypeSpecifierContext::createTrue(), $scope),
933-
);
868+
$funcName = $expr->expr->name->toLowerString();
869+
$arrayArgIndex = null;
870+
$sentinelType = null;
871+
$narrowToNonEmpty = false;
872+
873+
if ($funcName === 'array_search') {
874+
$arrayArgIndex = 1;
875+
$sentinelType = new ConstantBooleanType(false);
876+
} elseif ($funcName === 'array_find_key') {
877+
$arrayArgIndex = 0;
878+
$sentinelType = new NullType();
879+
$narrowToNonEmpty = true;
880+
}
881+
882+
if ($arrayArgIndex !== null && $sentinelType !== null) {
883+
$arrayArg = $expr->expr->getArgs()[$arrayArgIndex]->value;
884+
$arrayType = $scope->getType($arrayArg);
934885

935-
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
886+
if ($arrayType->isArray()->yes()) {
887+
if ($context->true()) {
888+
if ($narrowToNonEmpty) {
889+
$specifiedTypes = $specifiedTypes->unionWith(
890+
$this->create($arrayArg, new NonEmptyArrayType(), TypeSpecifierContext::createTrue(), $scope),
891+
);
892+
}
936893

937-
$specifiedTypes = $specifiedTypes->unionWith(
938-
$this->create($dimFetch, $arrayType->getIterableValueType(), TypeSpecifierContext::createTrue(), $scope),
939-
);
940-
} elseif ($expr->var instanceof Expr\Variable && is_string($expr->var->name)) {
941-
$keyType = $scope->getType($expr->expr);
942-
$nonNullKeyType = TypeCombinator::removeNull($keyType);
943-
if (!$nonNullKeyType instanceof NeverType && !$keyType->isNull()->yes()) {
944894
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
945-
$dimFetchString = $this->exprPrinter->printExpr($dimFetch);
946-
$keyExprString = $this->exprPrinter->printExpr($expr->var);
947-
948-
$holder = new ConditionalExpressionHolder(
949-
[$keyExprString => ExpressionTypeHolder::createYes($expr->var, $nonNullKeyType)],
950-
ExpressionTypeHolder::createYes($dimFetch, $arrayType->getIterableValueType()),
951-
);
952895

953896
$specifiedTypes = $specifiedTypes->unionWith(
954-
(new SpecifiedTypes([], []))->setNewConditionalExpressionHolders([
955-
$dimFetchString => [$holder->getKey() => $holder],
956-
]),
897+
$this->create($dimFetch, $arrayType->getIterableValueType(), TypeSpecifierContext::createTrue(), $scope),
957898
);
899+
} elseif ($expr->var instanceof Expr\Variable && is_string($expr->var->name)) {
900+
$keyType = $scope->getType($expr->expr);
901+
$narrowedKeyType = TypeCombinator::remove($keyType, $sentinelType);
902+
if (!$narrowedKeyType instanceof NeverType) {
903+
$specifiedTypes = $specifiedTypes->unionWith(
904+
$this->createArrayDimFetchConditionalExpressionHolder($expr->var, $arrayArg, $arrayType, $narrowedKeyType),
905+
);
906+
}
958907
}
959908
}
960909
}
@@ -2455,6 +2404,27 @@ public function create(
24552404
return $types;
24562405
}
24572406

2407+
private function createArrayDimFetchConditionalExpressionHolder(
2408+
Expr\Variable $keyVar,
2409+
Expr $arrayArg,
2410+
Type $arrayType,
2411+
Type $narrowedKeyType,
2412+
): SpecifiedTypes
2413+
{
2414+
$dimFetch = new ArrayDimFetch($arrayArg, $keyVar);
2415+
$dimFetchString = $this->exprPrinter->printExpr($dimFetch);
2416+
$keyExprString = $this->exprPrinter->printExpr($keyVar);
2417+
2418+
$holder = new ConditionalExpressionHolder(
2419+
[$keyExprString => ExpressionTypeHolder::createYes($keyVar, $narrowedKeyType)],
2420+
ExpressionTypeHolder::createYes($dimFetch, $arrayType->getIterableValueType()),
2421+
);
2422+
2423+
return (new SpecifiedTypes([], []))->setNewConditionalExpressionHolders([
2424+
$dimFetchString => [$holder->getKey() => $holder],
2425+
]);
2426+
}
2427+
24582428
private function createForExpr(
24592429
Expr $expr,
24602430
Type $type,

0 commit comments

Comments
 (0)