Skip to content

Commit 70a2fdd

Browse files
committed
Add Type::makeAllArrayKeysOptional() for preg_replace* array fast path
`ReplaceFunctionsDynamicReturnTypeExtension` previously rebuilt `ConstantArrayType`s by hand (via `ConstantArrayTypeBuilder`) to map each value through `getReplaceType()` while optionally marking every key optional. Replace that with polymorphic dispatch: $mapped = $arrayArgumentType->mapValueType(...); if ($keyShouldBeOptional) { $mapped = $mapped->makeAllArrayKeysOptional(); } This collapses the `getConstantArrays()` fast path with the generic `ArrayType` fallback, keeps non-emptiness/list-ness via the existing accessory dispatch, and removes the need for the extension to know about `ConstantArrayTypeBuilder`. `makeAllArrayKeysOptional()` is implemented across the standard set of `Type` callees: it marks every explicit key in a `ConstantArrayType` optional, is a no-op for `ArrayType` (already arbitrary subsets) and all accessories that do not track per-key optionality, drops `HasOffsetType`/`HasOffsetValueType` (their guarantee no longer holds), and distributes through unions/intersections.
1 parent 61943f9 commit 70a2fdd

17 files changed

Lines changed: 108 additions & 31 deletions

src/Type/Accessory/AccessoryArrayListType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,12 @@ public function mapKeyType(callable $cb): Type
280280
return $this;
281281
}
282282

283+
public function makeAllArrayKeysOptional(): Type
284+
{
285+
// Marking keys optional in an arbitrary list keeps it a list.
286+
return $this;
287+
}
288+
283289
public function changeKeyCaseArray(?int $case): Type
284290
{
285291
// List keys are integers; case-folding leaves them alone.

src/Type/Accessory/HasOffsetType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@ public function mapKeyType(callable $cb): Type
245245
return $this;
246246
}
247247

248+
public function makeAllArrayKeysOptional(): Type
249+
{
250+
// "Has offset X" is no longer guaranteed when X is now optional.
251+
return new MixedType();
252+
}
253+
248254
public function changeKeyCaseArray(?int $case): Type
249255
{
250256
// A string offset is itself case-folded; an int offset is unchanged.

src/Type/Accessory/HasOffsetValueType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,11 @@ public function mapKeyType(callable $cb): Type
333333
return $this;
334334
}
335335

336+
public function makeAllArrayKeysOptional(): Type
337+
{
338+
return new MixedType();
339+
}
340+
336341
public function changeKeyCaseArray(?int $case): Type
337342
{
338343
if (!$this->offsetType instanceof ConstantStringType) {

src/Type/Accessory/NonEmptyArrayType.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,13 @@ public function mapKeyType(callable $cb): Type
264264
return $this;
265265
}
266266

267+
public function makeAllArrayKeysOptional(): Type
268+
{
269+
// Without `ConstantArrayType` keys to mark optional, this is a no-op.
270+
// Non-emptiness is unrelated to per-key optionality and is preserved.
271+
return $this;
272+
}
273+
267274
public function changeKeyCaseArray(?int $case): Type
268275
{
269276
// Case-folding keys doesn't change the entry count.

src/Type/Accessory/OversizedArrayType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,11 @@ public function mapKeyType(callable $cb): Type
239239
return $this;
240240
}
241241

242+
public function makeAllArrayKeysOptional(): Type
243+
{
244+
return $this;
245+
}
246+
242247
public function changeKeyCaseArray(?int $case): Type
243248
{
244249
return $this;

src/Type/ArrayType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,12 @@ public function mapKeyType(callable $cb): Type
593593
return new ArrayType($cb($this->keyType), $this->getItemType());
594594
}
595595

596+
public function makeAllArrayKeysOptional(): Type
597+
{
598+
// `ArrayType` already models arbitrary key subsets.
599+
return $this;
600+
}
601+
596602
public function changeKeyCaseArray(?int $case): Type
597603
{
598604
$newKeyType = TypeTraverser::map($this->keyType, static function (Type $type, callable $traverse) use ($case): Type {

src/Type/Constant/ConstantArrayType.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,6 +2000,22 @@ public function mapKeyType(callable $cb): Type
20002000
return $this;
20012001
}
20022002

2003+
public function makeAllArrayKeysOptional(): Type
2004+
{
2005+
$keyCount = count($this->keyTypes);
2006+
if ($keyCount === 0) {
2007+
return $this;
2008+
}
2009+
2010+
return $this->recreate(
2011+
$this->keyTypes,
2012+
$this->valueTypes,
2013+
$this->nextAutoIndexes,
2014+
range(0, $keyCount - 1),
2015+
$this->isList,
2016+
);
2017+
}
2018+
20032019
public function changeKeyCaseArray(?int $case): Type
20042020
{
20052021
$builder = ConstantArrayTypeBuilder::createEmpty();

src/Type/IntersectionType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,11 @@ public function mapKeyType(callable $cb): Type
11881188
return $this->intersectTypes(static fn (Type $type): Type => $type->mapKeyType($cb));
11891189
}
11901190

1191+
public function makeAllArrayKeysOptional(): Type
1192+
{
1193+
return $this->intersectTypes(static fn (Type $type): Type => $type->makeAllArrayKeysOptional());
1194+
}
1195+
11911196
public function changeKeyCaseArray(?int $case): Type
11921197
{
11931198
return $this->intersectTypes(static fn (Type $type): Type => $type->changeKeyCaseArray($case));

src/Type/MixedType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,12 @@ public function mapKeyType(callable $cb): Type
335335
);
336336
}
337337

338+
public function makeAllArrayKeysOptional(): Type
339+
{
340+
// `mixed` is already arbitrary; nothing to weaken.
341+
return $this;
342+
}
343+
338344
public function changeKeyCaseArray(?int $case): Type
339345
{
340346
if ($this->isArray()->no()) {

src/Type/NeverType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,11 @@ public function mapKeyType(callable $cb): Type
399399
return new NeverType();
400400
}
401401

402+
public function makeAllArrayKeysOptional(): Type
403+
{
404+
return new NeverType();
405+
}
406+
402407
public function changeKeyCaseArray(?int $case): Type
403408
{
404409
return new NeverType();

0 commit comments

Comments
 (0)