Skip to content
This repository was archived by the owner on Sep 1, 2023. It is now read-only.

Commit d591d39

Browse files
committed
Support both shape behaviors
1 parent 7d3f375 commit d591d39

File tree

3 files changed

+44
-19
lines changed

3 files changed

+44
-19
lines changed

src/TypeSpec/__Private/ShapeSpec.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,19 @@
1515
use namespace HH\Lib\{C, Dict};
1616

1717
final class ShapeSpec extends TypeSpec<shape()> {
18+
const bool STRICT_SHAPES = HHVM_VERSION_ID >= 32300;
1819
private bool $allowUnknownFields;
1920

21+
private static function isOptionalField<Tany>(TypeSpec<Tany> $spec): bool {
22+
if ($spec->isOptional()) {
23+
return true;
24+
}
25+
if (self::STRICT_SHAPES) {
26+
return false;
27+
}
28+
return $spec instanceof NullableSpec;
29+
}
30+
2031
public function __construct(
2132
private dict<string, TypeSpec<mixed>> $inners,
2233
UnknownFieldsMode $unknown_fields,
@@ -39,7 +50,7 @@ public function coerceType(mixed $value): shape() {
3950
continue;
4051
}
4152

42-
if ($spec->isOptional()) {
53+
if (self::isOptionalField($spec)) {
4354
continue;
4455
}
4556

@@ -72,7 +83,7 @@ public function assertType(mixed $value): shape() {
7283
continue;
7384
}
7485

75-
if ($spec->isOptional()) {
86+
if (self::isOptionalField($spec)) {
7687
continue;
7788
}
7889

src/TypeSpec/__Private/from_type_structure.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ function from_type_structure<T>(TypeStructure<T> $ts): TypeSpec<T> {
114114
($_k, $field_ts) ==> from_type_structure($field_ts),
115115
($k, $_v) ==> $k,
116116
),
117-
($ts['allows_unknown_fields'] ?? false)
117+
($ts['allows_unknown_fields'] ?? !ShapeSpec::STRICT_SHAPES)
118118
? UnknownFieldsMode::ALLOW
119119
: UnknownFieldsMode::DENY,
120120
);

tests/TypeStructureTest.php

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public function testValidType<T>(TypeStructure<T> $ts, T $input): void {
198198

199199
public function getExampleInvalidTypes(
200200
): array<string, (mixed, mixed, vec<string>)> {
201-
return [
201+
$examples = [
202202
'"123" as int' => tuple(type_structure(C::class, 'TInt'), '123', vec[]),
203203
'1 as bool' => tuple(type_structure(C::class, 'TBool'), 1, vec[]),
204204
'int as float' => tuple(type_structure(C::class, 'TFloat'), 123, vec[]),
@@ -270,21 +270,11 @@ public function getExampleInvalidTypes(
270270
),
271271
vec['shape[someOptionalNullable]'],
272272
),
273-
'shape with missing nullable field' => tuple(
274-
type_structure(C::class, 'TFlatShape'),
275-
shape('someString' => 'foo'),
276-
vec['shape[someNullable]'],
277-
),
278273
'shape with incorrect field' => tuple(
279274
type_structure(C::class, 'TFlatShape'),
280275
shape('someString' => 123, 'someNullable' => null),
281276
vec['shape[someString]'],
282277
),
283-
'shape with extra fields' => tuple(
284-
type_structure(C::class, 'TShapeWithOneField'),
285-
shape('someString' => 'string', 'herp' => 'derp'),
286-
vec['shape[herp]'],
287-
),
288278
'shape with incorrect optional field' => tuple(
289279
type_structure(C::class, 'TFlatShape'),
290280
shape(
@@ -387,6 +377,20 @@ public function getExampleInvalidTypes(
387377
vec['keyset<T>'],
388378
),
389379
];
380+
381+
if (HHVM_VERSION_ID >= 32300) {
382+
$examples['shape with missing nullable field'] = tuple(
383+
type_structure(C::class, 'TFlatShape'),
384+
shape('someString' => 'foo'),
385+
vec['shape[someNullable]'],
386+
);
387+
$examples['shape with extra fields'] = tuple(
388+
type_structure(C::class, 'TShapeWithOneField'),
389+
shape('someString' => 'string', 'herp' => 'derp'),
390+
vec['shape[herp]'],
391+
);
392+
}
393+
return $examples;
390394
}
391395

392396
/**
@@ -450,17 +454,27 @@ public function getExampleValidCoercions(
450454
['foo' => 123, 'bar' => 456],
451455
dict['foo' => '123', 'bar' => '456'],
452456
),
453-
'shape with extra fields' => tuple(
454-
type_structure(C::class, 'TShapeWithOneField'),
455-
shape('someString' => 'foo', 'herp' => 'derp'),
456-
shape('someString' => 'foo'),
457-
),
458457
'shape with implicit subtyping and extra fields' => tuple(
459458
type_structure(C::class, 'TShapeWithOneFieldAndImplicitSubtypes'),
460459
shape('someString' => 'foo', 'herp' => 'derp'),
461460
shape('someString' => 'foo', 'herp' => 'derp'),
462461
),
463462
];
463+
464+
if (HHVM_VERSION_ID >= 32300) {
465+
$coercions['shape with extra fields'] = tuple(
466+
type_structure(C::class, 'TShapeWithOneField'),
467+
shape('someString' => 'foo', 'herp' => 'derp'),
468+
shape('someString' => 'foo'),
469+
);
470+
} else {
471+
$coercions['shape with extra fields'] = tuple(
472+
type_structure(C::class, 'TShapeWithOneField'),
473+
shape('someString' => 'foo', 'herp' => 'derp'),
474+
shape('someString' => 'foo', 'herp' => 'derp'),
475+
);
476+
}
477+
464478
return Dict\map(
465479
$this->getExampleValidTypes(),
466480
$tuple ==> {

0 commit comments

Comments
 (0)