Skip to content

Commit f1d04af

Browse files
Handle non constant unset
1 parent 405eecf commit f1d04af

File tree

1 file changed

+53
-34
lines changed

1 file changed

+53
-34
lines changed

src/Type/Constant/ConstantArrayType.php

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -741,36 +741,12 @@ public function unsetOffset(Type $offsetType): Type
741741
$k++;
742742
}
743743

744-
$newIsList = TrinaryLogic::createNo();
745-
// We're unsetting something that might not be on the array,
746-
// so it might still be a list (with PHPStan definition)
747-
// because the nextAutoIndexes will not change.
748-
if (!$this->isList->no() && in_array($i, $this->optionalKeys, true)) {
749-
$preserveIsList = true;
750-
$isListOnlyIfKeysAreOptional = false;
751-
foreach ($newKeyTypes as $k2 => $newKeyType2) {
752-
if (!$newKeyType2 instanceof ConstantIntegerType || $newKeyType2->getValue() !== $k2) {
753-
// We found a non-optional key that implies that the array is never a list.
754-
if (!in_array($k2, $newOptionalKeys, true)) {
755-
$preserveIsList = false;
756-
break;
757-
}
758-
759-
// The array can still be a list if all the following keys are also optional.
760-
$isListOnlyIfKeysAreOptional = true;
761-
continue;
762-
}
763-
764-
if ($isListOnlyIfKeysAreOptional && !in_array($k2, $newOptionalKeys, true)) {
765-
$preserveIsList = false;
766-
break;
767-
}
768-
}
769-
770-
if ($preserveIsList) {
771-
$newIsList = TrinaryLogic::createMaybe();
772-
}
773-
}
744+
$newIsList = $this->isListAfterUnset(
745+
$newKeyTypes,
746+
$newOptionalKeys,
747+
$this->isList,
748+
in_array($i, $this->optionalKeys, true),
749+
);
774750

775751
return new self($newKeyTypes, $newValueTypes, $this->nextAutoIndexes, $newOptionalKeys, $newIsList);
776752
}
@@ -801,21 +777,64 @@ public function unsetOffset(Type $offsetType): Type
801777
}
802778
}
803779

804-
return new self($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $optionalKeys, TrinaryLogic::createNo());
780+
$newIsList = $this->isListAfterUnset(
781+
$this->keyTypes,
782+
$optionalKeys,
783+
$this->isList,
784+
count($optionalKeys) === count($this->optionalKeys),
785+
);
786+
787+
return new self($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $optionalKeys, $newIsList);
805788
}
806789

807790
$optionalKeys = $this->optionalKeys;
808-
$isList = $this->isList;
809791
foreach ($this->keyTypes as $i => $keyType) {
810792
if (!$offsetType->isSuperTypeOf($keyType)->yes()) {
811793
continue;
812794
}
813795
$optionalKeys[] = $i;
814-
$isList = TrinaryLogic::createNo();
815796
}
816797
$optionalKeys = array_values(array_unique($optionalKeys));
817798

818-
return new self($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $optionalKeys, $isList);
799+
$newIsList = $this->isListAfterUnset(
800+
$this->keyTypes,
801+
$optionalKeys,
802+
$this->isList,
803+
count($optionalKeys) === count($this->optionalKeys),
804+
);
805+
806+
return new self($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $optionalKeys, $newIsList);
807+
}
808+
809+
/**
810+
* If we're unsetting something that might not be on the array, it might still be a list (with PHPStan definition)
811+
* because the nextAutoIndexes will not change.
812+
*/
813+
private function isListAfterUnset(array $newKeyTypes, array $newOptionalKeys, TrinaryLogic $arrayIsList, bool $unsetOptionalKey): TrinaryLogic
814+
{
815+
if (!$unsetOptionalKey || $arrayIsList->no()) {
816+
return TrinaryLogic::createNo();
817+
}
818+
819+
$isListOnlyIfKeysAreOptional = false;
820+
foreach ($newKeyTypes as $k2 => $newKeyType2) {
821+
if (!$newKeyType2 instanceof ConstantIntegerType || $newKeyType2->getValue() !== $k2) {
822+
// We found a non-optional key that implies that the array is never a list.
823+
if (!in_array($k2, $newOptionalKeys, true)) {
824+
return TrinaryLogic::createNo();
825+
}
826+
827+
// The array can still be a list if all the following keys are also optional.
828+
$isListOnlyIfKeysAreOptional = true;
829+
continue;
830+
}
831+
832+
if ($isListOnlyIfKeysAreOptional && !in_array($k2, $newOptionalKeys, true)) {
833+
return TrinaryLogic::createNo();
834+
}
835+
}
836+
837+
return TrinaryLogic::createMaybe();
819838
}
820839

821840
public function chunkArray(Type $lengthType, TrinaryLogic $preserveKeys): Type

0 commit comments

Comments
 (0)