Skip to content

Commit a0ece4d

Browse files
committed
Extract constant-array merging into a helper
Separate the constant-array strategy from operand validation and generic fallback so getResult() is easier to follow. Use null to signal fallback while preserving the existing resolved returns, key-order checks, and nonempty refinements. Keep the behavior tests unchanged.
1 parent 9b8e12b commit a0ece4d

1 file changed

Lines changed: 68 additions & 52 deletions

File tree

src/ArrayMergeType.php

Lines changed: 68 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -249,59 +249,10 @@ protected function getResult(): Type
249249
}
250250

251251
if ($nConstantArrays === count($types)) {
252-
$normalizedTypes = [];
253-
$allNormalizedTypesConstant = true;
252+
$result = self::tryMergeConstantArrays($types);
254253

255-
foreach ($types as $type) {
256-
$normalizedType = self::normalizeConstantArrayIntegerKeys($type);
257-
if (null === $normalizedType) {
258-
return new MixedType();
259-
}
260-
261-
$normalizedTypes[] = $normalizedType;
262-
if (!$normalizedType->isConstantArray()->yes()) {
263-
$allNormalizedTypesConstant = false;
264-
}
265-
266-
foreach ($type->getConstantArrays() as $constantArrayType) {
267-
if (self::hasUnknownExtraOffsets($constantArrayType)) {
268-
$allNormalizedTypesConstant = false;
269-
break;
270-
}
271-
}
272-
}
273-
274-
if ($allNormalizedTypesConstant) {
275-
$builder = ConstantArrayTypeBuilder::createEmpty();
276-
277-
foreach ($normalizedTypes as $normalizedType) {
278-
foreach (self::getConstantArrayKeyTypes($normalizedType) as $keyType) {
279-
$builder->setOffsetValueType(
280-
$keyType instanceof ConstantIntegerType ? null : $keyType,
281-
$normalizedType->getOffsetValueType($keyType),
282-
!$normalizedType->hasOffsetValueType($keyType)->yes(),
283-
);
284-
}
285-
}
286-
287-
$result = $builder->getArray();
288-
$constantResults = $result->getConstantArrays();
289-
290-
if (count($constantResults) !== 1 || self::hasConsistentKeyOrder($types, $constantResults[0])) {
291-
$emptyArray = ConstantArrayTypeBuilder::createEmpty()->getArray();
292-
293-
foreach ($types as $type) {
294-
// Benevolent unions can report nonempty while permitting an empty branch.
295-
if ($type->isSuperTypeOf($emptyArray)->no()) {
296-
return TypeCombinator::intersect($result, new NonEmptyArrayType());
297-
}
298-
}
299-
300-
return $result;
301-
}
302-
303-
// Use the generic fallback when order cannot be proven. Combining shapes
304-
// with TypeCombinator::union() can erase their different key orders.
254+
if (null !== $result) {
255+
return $result;
305256
}
306257
}
307258

@@ -378,6 +329,71 @@ protected function getResult(): Type
378329
return new ArrayType(new MixedType(true), new MixedType(true));
379330
}
380331

332+
/**
333+
* Returns null when the operands require the generic fallback.
334+
*
335+
* @param non-empty-list<Type> $types
336+
*/
337+
private static function tryMergeConstantArrays(array $types): ?Type
338+
{
339+
$normalizedTypes = [];
340+
$allNormalizedTypesConstant = true;
341+
342+
foreach ($types as $type) {
343+
$normalizedType = self::normalizeConstantArrayIntegerKeys($type);
344+
if (null === $normalizedType) {
345+
return new MixedType();
346+
}
347+
348+
$normalizedTypes[] = $normalizedType;
349+
if (!$normalizedType->isConstantArray()->yes()) {
350+
$allNormalizedTypesConstant = false;
351+
}
352+
353+
foreach ($type->getConstantArrays() as $constantArrayType) {
354+
if (self::hasUnknownExtraOffsets($constantArrayType)) {
355+
$allNormalizedTypesConstant = false;
356+
break;
357+
}
358+
}
359+
}
360+
361+
if ($allNormalizedTypesConstant) {
362+
$builder = ConstantArrayTypeBuilder::createEmpty();
363+
364+
foreach ($normalizedTypes as $normalizedType) {
365+
foreach (self::getConstantArrayKeyTypes($normalizedType) as $keyType) {
366+
$builder->setOffsetValueType(
367+
$keyType instanceof ConstantIntegerType ? null : $keyType,
368+
$normalizedType->getOffsetValueType($keyType),
369+
!$normalizedType->hasOffsetValueType($keyType)->yes(),
370+
);
371+
}
372+
}
373+
374+
$result = $builder->getArray();
375+
$constantResults = $result->getConstantArrays();
376+
377+
if (count($constantResults) !== 1 || self::hasConsistentKeyOrder($types, $constantResults[0])) {
378+
$emptyArray = ConstantArrayTypeBuilder::createEmpty()->getArray();
379+
380+
foreach ($types as $type) {
381+
// Benevolent unions can report nonempty while permitting an empty branch.
382+
if ($type->isSuperTypeOf($emptyArray)->no()) {
383+
return TypeCombinator::intersect($result, new NonEmptyArrayType());
384+
}
385+
}
386+
387+
return $result;
388+
}
389+
390+
// Use the generic fallback when order cannot be proven. Combining shapes
391+
// with TypeCombinator::union() can erase their different key orders.
392+
}
393+
394+
return null;
395+
}
396+
381397
private static function removeTopLevelNeverAlternatives(Type $type): Type
382398
{
383399
if (!($type instanceof UnionType) || $type instanceof TemplateType) {

0 commit comments

Comments
 (0)