|
13 | 13 | use function count;
|
14 | 14 | use function is_float;
|
15 | 15 | use function max;
|
| 16 | +use function range; |
16 | 17 |
|
17 | 18 | /** @api */
|
18 | 19 | class ConstantArrayTypeBuilder
|
@@ -59,67 +60,83 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt
|
59 | 60 | $offsetType = ArrayType::castToArrayKeyType($offsetType);
|
60 | 61 | }
|
61 | 62 |
|
62 |
| - if ( |
63 |
| - !$this->degradeToGeneralArray |
64 |
| - && ($offsetType instanceof ConstantIntegerType || $offsetType instanceof ConstantStringType) |
65 |
| - ) { |
66 |
| - /** @var ConstantIntegerType|ConstantStringType $keyType */ |
67 |
| - foreach ($this->keyTypes as $i => $keyType) { |
68 |
| - if ($keyType->getValue() === $offsetType->getValue()) { |
69 |
| - $this->valueTypes[$i] = $valueType; |
70 |
| - $this->optionalKeys = array_values(array_filter($this->optionalKeys, static fn (int $index): bool => $index !== $i)); |
71 |
| - return; |
| 63 | + if (!$this->degradeToGeneralArray) { |
| 64 | + if ($offsetType instanceof ConstantIntegerType || $offsetType instanceof ConstantStringType) { |
| 65 | + /** @var ConstantIntegerType|ConstantStringType $keyType */ |
| 66 | + foreach ($this->keyTypes as $i => $keyType) { |
| 67 | + if ($keyType->getValue() === $offsetType->getValue()) { |
| 68 | + $this->valueTypes[$i] = $valueType; |
| 69 | + $this->optionalKeys = array_values(array_filter($this->optionalKeys, static fn (int $index): bool => $index !== $i)); |
| 70 | + return; |
| 71 | + } |
72 | 72 | }
|
73 |
| - } |
74 | 73 |
|
75 |
| - $this->keyTypes[] = $offsetType; |
76 |
| - $this->valueTypes[] = $valueType; |
| 74 | + $this->keyTypes[] = $offsetType; |
| 75 | + $this->valueTypes[] = $valueType; |
77 | 76 |
|
78 |
| - if ($optional) { |
79 |
| - $this->optionalKeys[] = count($this->keyTypes) - 1; |
80 |
| - } |
| 77 | + if ($optional) { |
| 78 | + $this->optionalKeys[] = count($this->keyTypes) - 1; |
| 79 | + } |
81 | 80 |
|
82 |
| - /** @var int|float $newNextAutoIndex */ |
83 |
| - $newNextAutoIndex = $offsetType instanceof ConstantIntegerType |
84 |
| - ? max($this->nextAutoIndex, $offsetType->getValue() + 1) |
85 |
| - : $this->nextAutoIndex; |
86 |
| - if (!is_float($newNextAutoIndex)) { |
87 |
| - $this->nextAutoIndex = $newNextAutoIndex; |
| 81 | + /** @var int|float $newNextAutoIndex */ |
| 82 | + $newNextAutoIndex = $offsetType instanceof ConstantIntegerType |
| 83 | + ? max($this->nextAutoIndex, $offsetType->getValue() + 1) |
| 84 | + : $this->nextAutoIndex; |
| 85 | + if (!is_float($newNextAutoIndex)) { |
| 86 | + $this->nextAutoIndex = $newNextAutoIndex; |
| 87 | + } |
| 88 | + return; |
88 | 89 | }
|
89 |
| - return; |
90 |
| - } |
91 | 90 |
|
92 |
| - $scalarTypes = TypeUtils::getConstantScalars($offsetType); |
93 |
| - if (!$this->degradeToGeneralArray && count($scalarTypes) > 0) { |
94 |
| - $match = true; |
95 |
| - $valueTypes = $this->valueTypes; |
96 |
| - foreach ($scalarTypes as $scalarType) { |
97 |
| - $scalarOffsetType = ArrayType::castToArrayKeyType($scalarType); |
98 |
| - if (!$scalarOffsetType instanceof ConstantIntegerType && !$scalarOffsetType instanceof ConstantStringType) { |
99 |
| - throw new ShouldNotHappenException(); |
| 91 | + $scalarTypes = TypeUtils::getConstantScalars($offsetType); |
| 92 | + if (count($scalarTypes) === 0) { |
| 93 | + $integerRanges = TypeUtils::getIntegerRanges($offsetType); |
| 94 | + if (count($integerRanges) > 0) { |
| 95 | + foreach ($integerRanges as $integerRange) { |
| 96 | + if ($integerRange->getMin() === null) { |
| 97 | + break; |
| 98 | + } |
| 99 | + if ($integerRange->getMax() === null) { |
| 100 | + break; |
| 101 | + } |
| 102 | + |
| 103 | + foreach (range($integerRange->getMin(), $integerRange->getMax()) as $rangeValue) { |
| 104 | + $scalarTypes[] = new ConstantIntegerType($rangeValue); |
| 105 | + } |
| 106 | + } |
100 | 107 | }
|
101 |
| - $offsetMatch = false; |
| 108 | + } |
| 109 | + if (count($scalarTypes) > 0 && count($scalarTypes) < self::ARRAY_COUNT_LIMIT) { |
| 110 | + $match = true; |
| 111 | + $valueTypes = $this->valueTypes; |
| 112 | + foreach ($scalarTypes as $scalarType) { |
| 113 | + $scalarOffsetType = ArrayType::castToArrayKeyType($scalarType); |
| 114 | + if (!$scalarOffsetType instanceof ConstantIntegerType && !$scalarOffsetType instanceof ConstantStringType) { |
| 115 | + throw new ShouldNotHappenException(); |
| 116 | + } |
| 117 | + $offsetMatch = false; |
102 | 118 |
|
103 |
| - /** @var ConstantIntegerType|ConstantStringType $keyType */ |
104 |
| - foreach ($this->keyTypes as $i => $keyType) { |
105 |
| - if ($keyType->getValue() !== $scalarOffsetType->getValue()) { |
| 119 | + /** @var ConstantIntegerType|ConstantStringType $keyType */ |
| 120 | + foreach ($this->keyTypes as $i => $keyType) { |
| 121 | + if ($keyType->getValue() !== $scalarOffsetType->getValue()) { |
| 122 | + continue; |
| 123 | + } |
| 124 | + |
| 125 | + $valueTypes[$i] = TypeCombinator::union($valueTypes[$i], $valueType); |
| 126 | + $offsetMatch = true; |
| 127 | + } |
| 128 | + |
| 129 | + if ($offsetMatch) { |
106 | 130 | continue;
|
107 | 131 | }
|
108 | 132 |
|
109 |
| - $valueTypes[$i] = TypeCombinator::union($valueTypes[$i], $valueType); |
110 |
| - $offsetMatch = true; |
| 133 | + $match = false; |
111 | 134 | }
|
112 | 135 |
|
113 |
| - if ($offsetMatch) { |
114 |
| - continue; |
| 136 | + if ($match) { |
| 137 | + $this->valueTypes = $valueTypes; |
| 138 | + return; |
115 | 139 | }
|
116 |
| - |
117 |
| - $match = false; |
118 |
| - } |
119 |
| - |
120 |
| - if ($match) { |
121 |
| - $this->valueTypes = $valueTypes; |
122 |
| - return; |
123 | 140 | }
|
124 | 141 | }
|
125 | 142 |
|
|
0 commit comments